UNPKG

8.55 kBJavaScriptView Raw
1/**
2 * This task builds OpenLayers with the Closure Compiler.
3 */
4var path = require('path');
5
6var async = require('async');
7var closure = require('closure-util');
8var fs = require('fs-extra');
9var nomnom = require('nomnom');
10var temp = require('temp').track();
11var exec = require('child_process').exec;
12
13var generateExports = require('./generate-exports');
14
15var log = closure.log;
16var root = path.join(__dirname, '..');
17
18var umdWrapper = ';(function (root, factory) {\n' +
19 ' if (typeof exports === "object") {\n' +
20 ' module.exports = factory();\n' +
21 ' } else if (typeof define === "function" && define.amd) {\n' +
22 ' define([], factory);\n' +
23 ' } else {\n' +
24 ' root.ol = factory();\n' +
25 ' }\n' +
26 '}(this, function () {\n' +
27 ' var OPENLAYERS = {};\n' +
28 ' %output%\n' +
29 ' return OPENLAYERS.ol;\n' +
30 '}));\n';
31
32var version;
33
34
35/**
36 * Apply defaults and assert that a provided config object is valid.
37 * @param {Object} config Build configuration object.
38 * @param {function(Error)} callback Called with an error if config is invalid.
39 */
40function assertValidConfig(config, callback) {
41 process.nextTick(function() {
42 if (!Array.isArray(config.exports)) {
43 callback(new Error('Config missing "exports" array'));
44 return;
45 }
46 if (config.namespace && typeof config.namespace !== 'string') {
47 callback(new Error('Config "namespace" must be a string'));
48 return;
49 }
50 if (config.compile && typeof config.compile !== 'object') {
51 callback(new Error('Config "compile" must be an object'));
52 return;
53 }
54 if (config.jvm && !Array.isArray(config.jvm)) {
55 callback(new Error('Config "jvm" must be an array'));
56 return;
57 }
58 if (config.src && !Array.isArray(config.src)) {
59 callback(new Error('Config "src" must be an array'));
60 return;
61 }
62 if (config.umd) {
63 config.namespace = 'OPENLAYERS';
64 if (config.compile) {
65 config.compile.output_wrapper = umdWrapper;
66 if (version) {
67 if (!config.compile.define) {
68 config.compile.define = [];
69 }
70 config.compile.define.push('ol.VERSION=\'' + version + '\'');
71 }
72 }
73 }
74 callback(null);
75 });
76}
77
78
79/**
80 * Read the build configuration file.
81 * @param {string} configPath Path to config file.
82 * @param {function(Error, Object)} callback Callback.
83 */
84function readConfig(configPath, callback) {
85 fs.readFile(configPath, function(err, data) {
86 if (err) {
87 if (err.code === 'ENOENT') {
88 err = new Error('Unable to find config file: ' + configPath);
89 }
90 callback(err);
91 return;
92 }
93 var config;
94 try {
95 config = JSON.parse(String(data));
96 } catch (err2) {
97 callback(new Error('Trouble parsing config as JSON: ' + err2.message));
98 return;
99 }
100 callback(null, config);
101 });
102}
103
104
105/**
106 * Write the exports code to a temporary file.
107 * @param {string} exports Exports code.
108 * @param {function(Error, string)} callback Called with the path to the temp
109 * file (or any error).
110 */
111function writeExports(exports, callback) {
112 temp.open({prefix: 'exports', suffix: '.js'}, function(err, info) {
113 if (err) {
114 callback(err);
115 return;
116 }
117 log.verbose('build', 'Writing exports: ' + info.path);
118 fs.writeFile(info.path, exports, function(err) {
119 if (err) {
120 callback(err);
121 return;
122 }
123 fs.close(info.fd, function(err) {
124 if (err) {
125 callback(err);
126 return;
127 }
128 callback(null, info.path);
129 });
130 });
131 });
132}
133
134
135/**
136 * Get the list of sources sorted in dependency order.
137 * @param {Object} config Build configuration object.
138 * @param {string} exports Exports code (with goog.exportSymbol calls).
139 * @param {function(Error, Array.<string>)} callback Called with a list of paths
140 * or any error.
141 */
142function getDependencies(config, exports, callback) {
143 writeExports(exports, function(err, exportsPath) {
144 if (err) {
145 callback(err);
146 return;
147 }
148 log.info('ol', 'Parsing dependencies');
149 var options;
150 if (config.src) {
151 options = {
152 lib: config.src,
153 cwd: config.cwd
154 };
155 } else {
156 options = {
157 lib: ['src/**/*.js', 'build/ol.ext/*.js'],
158 cwd: root
159 };
160 }
161 closure.getDependencies(options, function(err, paths) {
162 if (err) {
163 callback(err);
164 return;
165 }
166 paths.push(exportsPath);
167 callback(null, paths);
168 });
169 });
170}
171
172
173/**
174 * Concatenate all sources.
175 * @param {Array.<string>} paths List of paths to source files.
176 * @param {function(Error, string)} callback Called with the concatenated
177 * output or any error.
178 */
179function concatenate(paths, callback) {
180 async.map(paths, fs.readFile, function(err, results) {
181 if (err) {
182 var msg = 'Trouble concatenating sources. ' + err.message;
183 callback(new Error(msg));
184 } else {
185 var parts = umdWrapper.split('%output%');
186 var src = parts[0] +
187 'var goog = this.goog = {};\n' +
188 'this.CLOSURE_NO_DEPS = true;\n' +
189 results.join('\n') +
190 'ol.VERSION = \'' + version + '\';\n' +
191 'OPENLAYERS.ol = ol;\n' +
192 parts[1];
193 callback(null, src);
194 }
195 });
196}
197
198
199/**
200 * Run the compiler.
201 * @param {Object} config Build configuration object.
202 * @param {Array.<string>} paths List of paths to source files.
203 * @param {function(Error, string)} callback Called with the compiled output or
204 * any error.
205 */
206function build(config, paths, callback) {
207 var options = {
208 compile: config.compile,
209 cwd: config.cwd || root,
210 jvm: config.jvm
211 };
212 if (!options.compile) {
213 log.info('ol', 'No compile options found. Concatenating ' +
214 paths.length + ' sources');
215 concatenate(paths, callback);
216 } else {
217 log.info('ol', 'Compiling ' + paths.length + ' sources');
218 paths = paths.concat('src/ol/typedefs.js');
219 options.compile.js = paths.concat(options.compile.js || []);
220 closure.compile(options, callback);
221 }
222}
223
224
225/**
226 * Gets the version from the Git tag.
227 * @param {function(Error, string)} callback Called with the output
228 * ready to be written into a file, or any error.
229 */
230function getVersion(callback) {
231 exec('git describe --tags', function(error, stdout, stderr) {
232 version = stdout.trim();
233 callback(null);
234 });
235}
236
237
238/**
239 * Adds a file header with the most recent Git tag.
240 * @param {string} compiledSource The compiled library.
241 * @param {function(Error, string)} callback Called with the output
242 * ready to be written into a file, or any error.
243 */
244function addHeader(compiledSource, callback) {
245 var header = '// OpenLayers. See https://openlayers.org/\n';
246 header += '// License: https://raw.githubusercontent.com/openlayers/' +
247 'openlayers/master/LICENSE.md\n';
248 if (version !== '') {
249 header += '// Version: ' + version + '\n';
250 }
251 callback(null, header + compiledSource);
252}
253
254
255/**
256 * Generate a build of the library.
257 * @param {Object} config Build configuration object. Must have an "exports"
258 * array and a "compile" object with options for the compiler.
259 * @param {function(Error, string)} callback Called with the compiled source
260 * or any error.
261 */
262function main(config, callback) {
263 async.waterfall([
264 getVersion,
265 assertValidConfig.bind(null, config),
266 generateExports.bind(null, config),
267 getDependencies.bind(null, config),
268 build.bind(null, config),
269 addHeader
270 ], callback);
271}
272
273
274/**
275 * If running this module directly, read the config file and call the main
276 * function.
277 */
278if (require.main === module) {
279 var options = nomnom.options({
280 config: {
281 position: 0,
282 required: true,
283 help: 'Path to JSON config file'
284 },
285 output: {
286 position: 1,
287 required: true,
288 help: 'Output file path'
289 },
290 loglevel: {
291 abbr: 'l',
292 choices: ['silly', 'verbose', 'info', 'warn', 'error'],
293 default: 'info',
294 help: 'Log level',
295 metavar: 'LEVEL'
296 }
297 }).parse();
298
299 /**
300 * Set the log level.
301 * @type {string}
302 */
303 log.level = options.loglevel;
304
305 // read the config, run the main function, and write the output file
306 async.waterfall([
307 readConfig.bind(null, options.config),
308 main,
309 fs.outputFile.bind(fs, options.output)
310 ], function(err) {
311 if (err) {
312 log.error(err.message);
313 process.exit(1);
314 } else {
315 process.exit(0);
316 }
317 });
318}
319
320
321/**
322 * Export main function.
323 */
324module.exports = main;