diff --git a/.travis.yml b/.travis.yml index cf56d6cc8d..2c1fa2ef5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,8 @@ before_script: - "rm src/ol/renderer/webgl/*shader.js" - "sh -e /etc/init.d/xvfb start" -script: "./build.py ci" +script: "make ci" after_success: - - "npm run test-coverage" + - "make test-coverage" - "cat coverage/lcov.info | ./node_modules/.bin/coveralls" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..47bd8f33dc --- /dev/null +++ b/Makefile @@ -0,0 +1,321 @@ +OS := $(shell uname) +BRANCH := $(shell git rev-parse --abbrev-ref HEAD) + +SRC_GLSL := $(shell find src -type f -name '*.glsl') +SRC_SHADER_JS := $(patsubst %.glsl,%shader.js,$(SRC_GLSL)) +SRC_JS := $(filter-out $(SRC_SHADER_JS),$(shell find src -name '*.js')) +SRC_JSDOC = $(shell find src -type f -name '*.jsdoc') + +SPEC_JS := $(shell find test/spec -type f -name '*.js') +SPEC_RENDERING_JS := $(shell find test_rendering/spec -name '*.js') + +EXAMPLES := $(shell find examples -type f) +EXAMPLES_HTML := $(filter-out examples/index.html,$(shell find examples -maxdepth 1 -type f -name '*.html')) +EXAMPLES_JS := $(patsubst %.html,%.js,$(EXAMPLES_HTML)) + +BUILD_EXAMPLES := $(subst examples,build/examples,$(EXAMPLES)) + +BUILD_HOSTED := build/hosted/$(BRANCH) +BUILD_HOSTED_EXAMPLES := $(addprefix $(BUILD_HOSTED)/,$(EXAMPLES)) +BUILD_HOSTED_EXAMPLES_JS := $(addprefix $(BUILD_HOSTED)/,$(EXAMPLES_JS)) + +CHECK_EXAMPLE_TIMESTAMPS = $(patsubst examples/%.html,build/timestamps/check-%-timestamp,$(EXAMPLES_HTML)) + +TASKS_JS := $(shell find tasks -name '*.js') + +CLOSURE_LIB = $(shell node -e 'process.stdout.write(require("closure-util").getLibraryPath())') + +ifeq ($(OS),Darwin) + STAT_COMPRESSED = stat -f ' compressed: %z bytes' + STAT_UNCOMPRESSED = stat -f 'uncompressed: %z bytes' +else + STAT_COMPRESSED = stat -c ' compressed: %s bytes' + STAT_UNCOMPRESSED = stat -c 'uncompressed: %s bytes' +endif + +.PHONY: default +default: help + +.PHONY: help +help: + @echo + @echo "The most common targets are:" + @echo + @echo "- install Install node dependencies" + @echo "- serve Start dev server for running examples and tests" + @echo "- test Run unit tests in the console" + @echo "- check Perform a number of checks on the code" + @echo "- clean Remove generated files" + @echo "- help Display this help message" + @echo + @echo "Other less frequently used targets are:" + @echo + @echo "- build Build ol.js, ol-debug.js, ol.js.map and ol.css" + @echo "- lint Check the code with the linter" + @echo "- ci Run the full continuous integration process" + @echo "- apidoc Build the API documentation using JSDoc" + @echo "- cleanall Remove all the build artefacts" + @echo "- check-deps Check if the required dependencies are installed" + @echo + +.PHONY: apidoc +apidoc: build/timestamps/jsdoc-$(BRANCH)-timestamp + +.PHONY: build +build: build/ol.css build/ol.js build/ol-debug.js build/ol.js.map + +.PHONY: check +check: lint build/ol.js test + +.PHONY: check-examples +check-examples: $(CHECK_EXAMPLE_TIMESTAMPS) + +.PHONY: check-deps +check-deps: EXECUTABLES = git node python java +check-deps: + @for exe in $(EXECUTABLES) ;\ + do \ + which $${exe} > /dev/null && \ + echo "Program $${exe} OK" || \ + echo "Program $${exe} MISSING!" ;\ + done ;\ + +.PHONY: ci +ci: lint build test test-rendering compile-examples check-examples apidoc + +.PHONY: compile-examples +compile-examples: build/compiled-examples/all.combined.js + +.PHONY: clean +clean: + rm -f build/timestamps/gjslint-timestamp + rm -f build/timestamps/jshint-timestamp + rm -f build/timestamps/check-*-timestamp + rm -f build/ol.css + rm -f build/ol.js + rm -f build/ol.js.map + rm -f build/ol-debug.js + rm -f build/test_requires.js + rm -f build/test_rendering_requires.js + rm -rf build/examples + rm -rf build/compiled-examples + rm -rf $(BUILD_HOSTED) + +.PHONY: cleanall +cleanall: + rm -rf build + +.PHONY: css +css: build/ol.css + +.PHONY: examples +examples: $(BUILD_EXAMPLES) + +.PHONY: install +install: build/timestamps/node-modules-timestamp + +.PHONY: lint +lint: build/timestamps/gjslint-timestamp build/timestamps/jshint-timestamp \ + build/timestamps/check-requires-timestamp \ + build/timestamps/check-whitespace-timestamp + +.PHONY: npm-install +npm-install: build/timestamps/node-modules-timestamp + +.PHONY: shaders +shaders: $(SRC_SHADER_JS) + +.PHONY: serve +serve: build/test_requires.js build/test_rendering_requires.js + node tasks/serve.js + +.PHONY: test +test: build/timestamps/node-modules-timestamp build/test_requires.js + node tasks/test.js + +.PHONY: test-coverage +test-coverage: build/timestamps/node-modules-timestamp + node tasks/test-coverage.js + +.PHONY: test-rendering +test-rendering: build/timestamps/node-modules-timestamp \ + build/test_rendering_requires.js + @rm -rf build/slimerjs-profile + @mkdir -p build/slimerjs-profile + @cp -r test_rendering/slimerjs-profile/* build/slimerjs-profile/ + node tasks/test-rendering.js + +.PHONY: host-examples +host-examples: $(BUILD_HOSTED_EXAMPLES) \ + $(BUILD_HOSTED)/build/ol.js \ + $(BUILD_HOSTED)/build/ol-debug.js \ + $(BUILD_HOSTED)/css/ol.css \ + $(BUILD_HOSTED)/examples/loader.js \ + $(BUILD_HOSTED)/build/ol-deps.js + +.PHONY: host-libraries +host-libraries: build/timestamps/node-modules-timestamp + @rm -rf $(BUILD_HOSTED)/closure-library + @mkdir -p $(BUILD_HOSTED)/closure-library + @cp -r $(CLOSURE_LIB)/* $(BUILD_HOSTED)/closure-library/ + @rm -rf $(BUILD_HOSTED)/ol/ol + @mkdir -p $(BUILD_HOSTED)/ol/ol + @cp -r src/ol/* $(BUILD_HOSTED)/ol/ol/ + @rm -rf $(BUILD_HOSTED)/ol.ext + @mkdir -p $(BUILD_HOSTED)/ol.ext + @cp -r build/ol.ext/* $(BUILD_HOSTED)/ol.ext/ + +$(BUILD_EXAMPLES): $(EXAMPLES) + @mkdir -p $(@D) + @node tasks/build-examples.js + +build/timestamps/check-%-timestamp: $(BUILD_HOSTED)/examples/%.html \ + $(BUILD_HOSTED)/examples/%.js \ + $(filter $(BUILD_HOSTED)/examples/resources/%,$(BUILD_HOSTED_EXAMPLES)) \ + $(filter $(BUILD_HOSTED)/examples/data/%,$(BUILD_HOSTED_EXAMPLES)) \ + $(BUILD_HOSTED)/examples/loader.js \ + $(BUILD_HOSTED)/build/ol.js \ + $(BUILD_HOSTED)/css/ol.css + @mkdir -p $(@D) + ./node_modules/.bin/phantomjs --ssl-protocol=any --ignore-ssl-errors=true bin/check-example.js $(addsuffix ?mode=advanced, $<) + @touch $@ + +build/timestamps/check-requires-timestamp: $(SRC_JS) $(EXAMPLES_JS) \ + $(SRC_SHADER_JS) $(SPEC_JS) \ + $(SPEC_RENDERING JS) + @mkdir -p $(@D) + @python bin/check-requires.py $(CLOSURE_LIB) $^ + @touch $@ + +build/timestamps/check-whitespace-timestamp: $(SRC_JS) $(EXAMPLES_JS) \ + $(SPEC_JS) $(SPEC_RENDERING JS) \ + $(SRC_JSDOC) + @mkdir -p $(@D) + @python bin/check-whitespace.py $^ + @touch $@ + +build/compiled-examples/all.js: $(EXAMPLES_JS) + @mkdir -p $(@D) + @python bin/combine-examples.py $^ > $@ + +build/compiled-examples/all.combined.js: config/examples-all.json build/compiled-examples/all.js \ + $(SRC_JS) $(SRC_SHADER_JS) \ + build/timestamps/node-modules-timestamp + @mkdir -p $(@D) + node tasks/build.js $< $@ + +build/compiled-examples/%.json: config/example.json build/examples/%.js \ + build/timestamps/node-modules-timestamp + @mkdir -p $(@D) + @sed -e 's|{{id}}|$*|' $< > $@ + +build/compiled-examples/%.combined.js: build/compiled-examples/%.json \ + $(SRC_JS) $(SRC_SHADER_JS) \ + build/timestamps/node-modules-timestamp + @mkdir -p $(@D) + node tasks/build.js $< $@ + +build/timestamps/jsdoc-$(BRANCH)-timestamp: config/jsdoc/api/index.md \ + config/jsdoc/api/conf.json $(SRC_JS) \ + $(SRC_SHADER_JS) \ + $(shell find config/jsdoc/api/template -type f) \ + build/timestamps/node-modules-timestamp + @mkdir -p $(@D) + @rm -rf $(BUILD_HOSTED)/apidoc + ./node_modules/.bin/jsdoc config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -d $(BUILD_HOSTED)/apidoc + @touch $@ + +build/timestamps/gjslint-timestamp: $(SRC_JS) $(SPEC_JS) $(SPEC_RENDERING_JS) \ + $(EXAMPLES_JS) + @mkdir -p $(@D) + @echo "Running gjslint..." + @gjslint --jslint_error=all --custom_jsdoc_tags=event,fires,function,classdesc,api,observable --strict $? + @touch $@ + +$(BUILD_HOSTED_EXAMPLES_JS): $(BUILD_HOSTED)/examples/%.js: build/examples/%.js + @mkdir -p $(@D) + @python bin/split-example.py $< $(@D) + +$(BUILD_HOSTED)/examples/loader.js: bin/loader_hosted_examples.js + @mkdir -p $(@D) + @cp $< $@ + +$(BUILD_HOSTED)/examples/%: build/examples/% + @mkdir -p $(@D) + @cp $< $@ + +$(BUILD_HOSTED)/build/ol.js: build/ol.js + @mkdir -p $(@D) + @cp $< $@ + +$(BUILD_HOSTED)/build/ol-debug.js: build/ol-debug.js + @mkdir -p $(@D) + @cp $< $@ + +$(BUILD_HOSTED)/css/ol.css: build/ol.css + @mkdir -p $(@D) + @cp $< $@ + +$(BUILD_HOSTED)/build/ol-deps.js: host-libraries + @mkdir -p $(@D) + @python $(CLOSURE_LIB)/closure/bin/build/depswriter.py \ + --root_with_prefix "src ../../../ol" \ + --root_with_prefix "build/ol.ext ../../../ol.ext" \ + --root $(BUILD_HOSTED)/closure-library/closure/goog \ + --root_with_prefix "$(BUILD_HOSTED)/closure-library/third_party ../../third_party" \ + --output_file $@ + +build/timestamps/jshint-timestamp: $(SRC_JS) $(SPEC_JS) $(SPEC_RENDERING_JS) \ + $(TASKS_JS) $(EXAMPLES_JS) \ + examples/resources/common.js \ + build/timestamps/node-modules-timestamp + @mkdir -p $(@D) + @echo "Running jshint..." + @./node_modules/.bin/jshint --verbose $? + @touch $@ + +build/timestamps/node-modules-timestamp: package.json + @mkdir -p $(@D) + npm install + @touch $@ + +build/ol.css: css/ol.css build/timestamps/node-modules-timestamp + @mkdir -p $(@D) + @echo "Running cleancss..." + @./node_modules/.bin/cleancss $< > $@ + +build/ol.js: config/ol.json $(SRC_JS) $(SRC_SHADER_JS) \ + build/timestamps/node-modules-timestamp + @mkdir -p $(@D) + node tasks/build.js $< $@ + @$(STAT_UNCOMPRESSED) $@ + @cp $@ /tmp/ + @gzip /tmp/ol.js + @$(STAT_COMPRESSED) /tmp/ol.js.gz + @rm /tmp/ol.js.gz + +build/ol.js.map: config/ol.json $(SRC_JS) $(SRC_SHADER_JS) \ + build/timestamps/node-modules-timestamp + @mkdir -p $(@D) + node tasks/build.js $< $@ + +build/ol-debug.js: config/ol-debug.json $(SRC_JS) $(SRC_SHADER_JS) \ + build/timestamps/node-modules-timestamp + @mkdir -p $(@D) + node tasks/build.js $< $@ + @$(STAT_UNCOMPRESSED) $@ + @cp $@ /tmp/ + @gzip /tmp/ol-debug.js + @$(STAT_COMPRESSED) /tmp/ol-debug.js.gz + @rm /tmp/ol-debug.js.gz + +build/test_requires.js: $(SPEC_JS) $(SRC_JS) + @mkdir -p $(@D) + @node tasks/generate-requires.js $^ > $@ + +build/test_rendering_requires.js: $(SPEC_RENDERING_JS) + @mkdir -p $(@D) + @node tasks/generate-requires.js $^ > $@ + +%shader.js: %.glsl src/ol/webgl/shader.mustache bin/pyglslunit.py + @python bin/pyglslunit.py --input $< --template src/ol/webgl/shader.mustache --output $@ diff --git a/bin/check-requires.py b/bin/check-requires.py new file mode 100644 index 0000000000..6422985ff2 --- /dev/null +++ b/bin/check-requires.py @@ -0,0 +1,190 @@ +import os +import logging +import re +import sys + +logging.basicConfig(format='%(asctime)s %(name)s: %(message)s', + level=logging.INFO) + +logger = logging.getLogger('check-requires') + + +class Node(object): + + def __init__(self): + self.present = False + self.children = {} + + def _build_re(self, key): + if key == '*': + assert len(self.children) == 0 + # We want to match `.doIt` but not `.SomeClass` or `.more.stuff` + return '(?=\\.[a-z]\\w*\\b(?!\\.))' + elif len(self.children) == 1: + child_key, child = next(self.children.iteritems()) + child_re = child._build_re(child_key) + if child_key != '*': + child_re = '\\.' + child_re + if self.present: + return key + '(' + child_re + ')?' + else: + return key + child_re + elif self.children: + children_re = '(?:' + '|'.join( + ('\\.' if k != '*' else '') + self.children[k]._build_re(k) + for k in sorted(self.children.keys())) + ')' + if self.present: + return key + children_re + '?' + else: + return key + children_re + else: + assert self.present + return key + + def build_re(self, key): + return re.compile('\\b' + self._build_re(key) + '\\b') + + +def ifind(*paths): + """ifind is an iterative version of os.walk, yielding all walked paths and + normalizing paths to use forward slashes.""" + for path in paths: + for dirpath, dirnames, names in os.walk(path): + for name in names: + if os.sep == '/': + yield os.path.join(dirpath, name) + else: + yield '/'.join(dirpath.split(os.sep) + [name]) + + +def _strip_comments(lines): + # FIXME this is a horribe hack, we should use a proper JavaScript parser + # here + in_multiline_comment = False + lineno = 0 + for line in lines: + lineno += 1 + if in_multiline_comment: + index = line.find('*/') + if index != -1: + in_multiline_comment = False + line = line[index + 2:] + if not in_multiline_comment: + line = re.sub(r'//[^\n]*', '', line) + line = re.sub(r'/\*.*?\*/', '', line) + index = line.find('/*') + if index != -1: + yield lineno, line[:index] + in_multiline_comment = True + else: + yield lineno, line + + +def check_requires(closure_lib, *filenames): + unused_count = 0 + all_provides = set() + + for filename in ifind(closure_lib): + if filename.endswith('.js'): + if not re.match(r'.*/closure/goog/', filename): + continue + # Skip goog.i18n because it contains so many modules that it causes + # the generated regular expression to exceed Python's limits + if re.match(r'.*/closure/goog/i18n/', filename): + continue + for line in open(filename, 'rU'): + m = re.match(r'goog.provide\(\'(.*)\'\);', line) + if m: + all_provides.add(m.group(1)) + + for filename in sorted(filenames): + require_linenos = {} + uses = set() + lines = open(filename, 'rU').readlines() + for lineno, line in _strip_comments(lines): + m = re.match(r'goog.provide\(\'(.*)\'\);', line) + if m: + all_provides.add(m.group(1)) + continue + m = re.match(r'goog.require\(\'(.*)\'\);', line) + if m: + require_linenos[m.group(1)] = lineno + continue + ignore_linenos = require_linenos.values() + for lineno, line in enumerate(lines): + if lineno in ignore_linenos: + continue + for require in require_linenos.iterkeys(): + if require in line: + uses.add(require) + for require in sorted(set(require_linenos.keys()) - uses): + logger.info('%s:%d: unused goog.require: %r' % ( + filename, require_linenos[require], require)) + unused_count += 1 + + all_provides.discard('ol') + all_provides.discard('ol.MapProperty') + + root = Node() + for provide in all_provides: + node = root + for component in provide.split('.'): + if component not in node.children: + node.children[component] = Node() + node = node.children[component] + if component[0].islower(): + # We've arrived at a namespace provide like `ol.foo`. + # In this case, we want to match uses like `ol.foo.doIt()` but + # not match things like `new ol.foo.SomeClass()`. + # For this purpose, we use the special wildcard key for the child. + node.children['*'] = Node() + else: + node.present = True + provide_res = [child.build_re(key) + for key, child in root.children.iteritems()] + missing_count = 0 + for filename in sorted(filenames): + provides = set() + requires = set() + uses = set() + uses_linenos = {} + for lineno, line in _strip_comments(open(filename, 'rU')): + m = re.match(r'goog.provide\(\'(.*)\'\);', line) + if m: + provides.add(m.group(1)) + continue + m = re.match(r'goog.require\(\'(.*)\'\);', line) + if m: + requires.add(m.group(1)) + continue + while True: + for provide_re in provide_res: + m = provide_re.search(line) + if m: + uses.add(m.group()) + uses_linenos[m.group()] = lineno + line = line[:m.start()] + line[m.end():] + break + else: + break + if filename == 'src/ol/renderer/layerrenderer.js': + uses.discard('ol.renderer.Map') + m = re.match( + r'src/ol/renderer/(\w+)/\1(\w*)layerrenderer\.js\Z', filename) + if m: + uses.discard('ol.renderer.Map') + uses.discard('ol.renderer.%s.Map' % (m.group(1),)) + missing_requires = uses - requires - provides + if missing_requires: + for missing_require in sorted(missing_requires): + logger.info("%s:%d missing goog.require('%s')" % + (filename, uses_linenos[missing_require], + missing_require)) + missing_count += 1 + if unused_count or missing_count: + logger.error('%d unused goog.requires, %d missing goog.requires' % + (unused_count, missing_count)) + + +if __name__ == "__main__": + check_requires(*sys.argv[1:]) diff --git a/bin/check-whitespace.py b/bin/check-whitespace.py new file mode 100644 index 0000000000..4ce5996bf6 --- /dev/null +++ b/bin/check-whitespace.py @@ -0,0 +1,44 @@ +import logging +import re +import sys + +logging.basicConfig(format='%(asctime)s %(name)s: %(message)s', + level=logging.INFO) + +logger = logging.getLogger('check-whitespace') + +CR_RE = re.compile(r'\r') +LEADING_WHITESPACE_RE = re.compile(r'\s+') +TRAILING_WHITESPACE_RE = re.compile(r'\s+\n\Z') +NO_NEWLINE_RE = re.compile(r'[^\n]\Z') +ALL_WHITESPACE_RE = re.compile(r'\s+\Z') + + +def check_whitespace(*filenames): + errors = 0 + for filename in sorted(filenames): + whitespace = False + for lineno, line in enumerate(open(filename, 'rU')): + if lineno == 0 and LEADING_WHITESPACE_RE.match(line): + logger.info('%s:%d: leading whitespace', filename, lineno + 1) + errors += 1 + if CR_RE.search(line): + logger.info('%s:%d: carriage return character in line', + filename, lineno + 1) + errors += 1 + if TRAILING_WHITESPACE_RE.search(line): + logger.info('%s:%d: trailing whitespace', filename, lineno + 1) + errors += 1 + if NO_NEWLINE_RE.search(line): + logger.info('%s:%d: no newline at end of file', filename, + lineno + 1) + errors += 1 + whitespace = ALL_WHITESPACE_RE.match(line) + if whitespace: + logger.info('%s: trailing whitespace at end of file', filename) + errors += 1 + if errors: + logger.error('%d whitespace errors' % (errors,)) + +if __name__ == "__main__": + check_whitespace(*sys.argv[1:]) diff --git a/bin/split-example.py b/bin/split-example.py new file mode 100644 index 0000000000..8f8b92d4ce --- /dev/null +++ b/bin/split-example.py @@ -0,0 +1,39 @@ +import os +import re +import sys + + +def split_example_file(example, dst_dir): + lines = open(example, 'rU').readlines() + + target_lines = [] + target_require_lines = [] + + found_requires = False + found_code = False + for line in lines: + m = re.match(r'goog.require\(\'(.*)\'\);', line) + if m: + found_requires = True + target_require_lines.append(line) + elif found_requires: + if found_code or line not in ('\n', '\r\n'): + found_code = True + target_lines.append(line) + + target = open( + os.path.join(dst_dir, os.path.basename(example)), 'wb') + target_require = open( + os.path.join(dst_dir, os.path.basename(example) + .replace('.js', '-require.js')), + 'wb') + + target.writelines(target_lines) + target.close() + + target_require.writelines(target_require_lines) + target_require.close() + + +if __name__ == '__main__': + split_example_file(*sys.argv[1:]) diff --git a/config/example.json b/config/example.json new file mode 100644 index 0000000000..b0b8b79f6f --- /dev/null +++ b/config/example.json @@ -0,0 +1,79 @@ +{ + "exports": [], + "src": [ + "src/**/*.js", + "build/ol.ext/*.js", + "build/examples/{{id}}.js" + ], + "compile": { + "js": [ + "externs/olx.js", + "externs/oli.js" + ], + "externs": [ + "externs/bingmaps.js", + "externs/bootstrap.js", + "externs/closure-compiler.js", + "externs/example.js", + "externs/fastclick.js", + "externs/geojson.js", + "externs/jquery-1.9.js", + "externs/proj4js.js", + "externs/tilejson.js", + "externs/topojson.js", + "externs/vbarray.js" + ], + "define": [ + "goog.array.ASSUME_NATIVE_FUNCTIONS=true", + "goog.dom.ASSUME_STANDARDS_MODE=true", + "goog.json.USE_NATIVE_JSON=true", + "goog.DEBUG=false" + ], + "jscomp_error": [ + "accessControls", + "ambiguousFunctionDecl", + "checkEventfulObjectDisposal", + "checkRegExp", + "checkStructDictInheritance", + "checkTypes", + "checkVars", + "const", + "constantProperty", + "deprecated", + "duplicateMessage", + "es3", + "es5Strict", + "externsValidation", + "fileoverviewTags", + "globalThis", + "internetExplorerChecks", + "invalidCasts", + "misplacedTypeAnnotation", + "missingGetCssName", + "missingProperties", + "missingProvide", + "missingRequire", + "missingReturn", + "newCheckTypes", + "nonStandardJsDocs", + "suspiciousCode", + "strictModuleDepCheck", + "typeInvalidation", + "undefinedNames", + "undefinedVars", + "uselessCode", + "visibility" + ], + "jscomp_off": [ + "unknownDefines" + ], + "extra_annotation_name": [ + "api", "observable" + ], + "compilation_level": "ADVANCED", + "warning_level": "VERBOSE", + "output_wrapper": "(function(){%output%})();", + "use_types_for_optimization": true, + "manage_closure_dependencies": true + } +} diff --git a/package.json b/package.json index 593d055275..856e507df0 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,7 @@ "install": "node tasks/install.js", "postinstall": "closure-util update", "start": "node tasks/serve.js", - "test": "node tasks/test.js", - "test-coverage": "node tasks/test-coverage.js" + "test": "node tasks/test.js" }, "main": "dist/ol.js", "repository": { diff --git a/tasks/generate-requires.js b/tasks/generate-requires.js new file mode 100644 index 0000000000..3c190df2cf --- /dev/null +++ b/tasks/generate-requires.js @@ -0,0 +1,40 @@ +var fs = require('fs'); + +// The number of files that we need to generate goog.require's for. +var numFiles = process.argv.length - 1; + +/** + * Object used a set of found goog.provide's. + * @type {Object.} + */ +var requires = {}; + +process.argv.forEach(function(val, index, array) { + + if (index === 0) { + return; + } + + fs.readFile(val, function(err, data) { + if (err) { + return; + } + + var re = new RegExp('goog\\.provide\\(\'(.*)\'\\);'); + + data.toString().split('\n').forEach(function(line) { + var match = line.match(re); + if (match) { + requires[match[1]] = true; + } + }); + + if (--numFiles === 0) { + Object.keys(requires).sort().forEach(function(key) { + process.stdout.write('goog.require(\'' + key + '\');\n'); + }); + } + + }); + +});