UNPKG

9.02 kBJavaScriptView Raw
1// TODO: Should deprecate and move into marko/cli
2
3/* eslint-disable no-console */
4
5var fs = require("fs");
6var nodePath = require("path");
7var cwd = process.cwd();
8var resolveFrom = require("resolve-from");
9
10// Try to use the Marko compiler installed with the project
11var markoCompilerPath;
12const markocPkgVersion = require("../package.json").version;
13
14var markoPkgVersion;
15try {
16 var markoPkgPath = resolveFrom(process.cwd(), "marko/package.json");
17 markoPkgVersion = require(markoPkgPath).version;
18} catch (e) {
19 /* ignore error */
20}
21
22try {
23 markoCompilerPath = resolveFrom(process.cwd(), "marko/compiler");
24} catch (e) {
25 /* ignore error */
26}
27
28var markoCompiler;
29
30if (markoCompilerPath) {
31 markoCompiler = require(markoCompilerPath);
32} else {
33 markoCompiler = require("../compiler");
34}
35
36var Minimatch = require("minimatch").Minimatch;
37
38var appModulePath = require("app-module-path");
39
40markoCompiler.defaultOptions.checkUpToDate = false;
41
42var mmOptions = {
43 matchBase: true,
44 dot: true,
45 flipNegate: true
46};
47
48function relPath(path) {
49 if (path.startsWith(cwd)) {
50 return path.substring(cwd.length + 1);
51 }
52}
53
54var args = require("argly")
55 .createParser({
56 "--help": {
57 type: "boolean",
58 description: "Show this help message"
59 },
60 "--files --file -f *": {
61 type: "string[]",
62 description: "A set of directories or files to compile"
63 },
64 "--ignore -i": {
65 type: "string[]",
66 description: 'An ignore rule (default: --ignore "/node_modules" ".*")'
67 },
68 "--clean -c": {
69 type: "boolean",
70 description: "Clean all of the *.marko.js files"
71 },
72 "--force": {
73 type: "boolean",
74 description: "Force template recompilation even if unchanged"
75 },
76 "--paths -p": {
77 type: "string[]",
78 description:
79 "Additional directories to add to the Node.js module search path"
80 },
81 "--quiet -q": {
82 type: "boolean",
83 description: "Only print warnings and errors"
84 },
85 "--vdom -V": {
86 type: "boolean",
87 description: "VDOM output"
88 },
89 "--version -v": {
90 type: "boolean",
91 description: "Print markoc and marko compiler versions to the console"
92 }
93 })
94 .usage("Usage: $0 <pattern> [options]")
95 .example("Compile a single template", "$0 template.marko")
96 .example("Compile all templates in the current directory", "$0 .")
97 .example("Compile multiple templates", "$0 template.marko src/ foo/")
98 .example(
99 "Delete all *.marko.js files in the current directory",
100 "$0 . --clean"
101 )
102 .validate(function(result) {
103 if (result.help) {
104 this.printUsage();
105 process.exit(0);
106 } else if (result.version) {
107 console.log("markoc@" + markocPkgVersion);
108
109 if (markoPkgVersion) {
110 console.log("marko@" + markoPkgVersion);
111 }
112
113 process.exit(0);
114 } else if (!result.files || result.files.length === 0) {
115 this.printUsage();
116 process.exit(1);
117 }
118 })
119 .onError(function(err) {
120 this.printUsage();
121
122 if (err) {
123 console.log();
124 console.log(err);
125 }
126
127 process.exit(1);
128 })
129 .parse();
130
131var output = "html";
132
133var isForBrowser = false;
134
135if (args.vdom) {
136 output = "vdom";
137 isForBrowser = true;
138}
139
140var compileOptions = {
141 output: output,
142 browser: isForBrowser,
143 compilerType: "markoc",
144 compilerVersion: markoPkgVersion || markocPkgVersion
145};
146
147var force = args.force;
148if (force) {
149 markoCompiler.defaultOptions.checkUpToDate = false;
150}
151
152var paths = args.paths;
153if (paths && paths.length) {
154 paths.forEach(function(path) {
155 appModulePath.addPath(nodePath.resolve(cwd, path));
156 });
157}
158
159var ignoreRules = args.ignore;
160
161if (!ignoreRules) {
162 ignoreRules = ["/node_modules", ".*"];
163}
164
165ignoreRules = ignoreRules.filter(function(s) {
166 s = s.trim();
167 return s && !s.match(/^#/);
168});
169
170ignoreRules = ignoreRules.map(function(pattern) {
171 return new Minimatch(pattern, mmOptions);
172});
173
174function isIgnored(path, dir, stat) {
175 if (path.startsWith(dir)) {
176 path = path.substring(dir.length);
177 }
178
179 path = path.replace(/\\/g, "/");
180
181 var ignore = false;
182 var ignoreRulesLength = ignoreRules.length;
183 for (var i = 0; i < ignoreRulesLength; i++) {
184 var rule = ignoreRules[i];
185
186 var match = rule.match(path);
187
188 if (!match && stat && stat.isDirectory()) {
189 try {
190 stat = fs.statSync(path);
191 } catch (e) {
192 /* ignore error */
193 }
194
195 if (stat && stat.isDirectory()) {
196 match = rule.match(path + "/");
197 }
198 }
199
200 if (match) {
201 if (rule.negate) {
202 ignore = false;
203 } else {
204 ignore = true;
205 }
206 }
207 }
208
209 return ignore;
210}
211
212function walk(files, options, done) {
213 if (!files || files.length === 0) {
214 done("No files provided");
215 }
216
217 var pending = 0;
218
219 if (!Array.isArray(files)) {
220 files = [files];
221 }
222
223 var fileCallback = options.file;
224 var context = {
225 errors: [],
226 beginAsync: function() {
227 pending++;
228 },
229 endAsync: function(err) {
230 if (err) {
231 this.errors.push(err);
232 }
233
234 pending--;
235
236 if (pending === 0) {
237 if (this.errors.length) {
238 done(this.errors);
239 } else {
240 done(null);
241 }
242 }
243 }
244 };
245
246 function doWalk(dir) {
247 context.beginAsync();
248 fs.readdir(dir, function(err, list) {
249 if (err) {
250 return context.endAsync(err);
251 }
252
253 if (list.length) {
254 list.forEach(function(basename) {
255 var file = nodePath.join(dir, basename);
256
257 context.beginAsync();
258 fs.stat(file, function(err, stat) {
259 if (err) {
260 return context.endAsync(err);
261 }
262
263 if (!isIgnored(file, dir, stat)) {
264 if (stat && stat.isDirectory()) {
265 doWalk(file);
266 } else {
267 fileCallback(file, context);
268 }
269 }
270
271 context.endAsync();
272 });
273 });
274 }
275
276 context.endAsync();
277 });
278 }
279
280 for (var i = 0; i < files.length; i++) {
281 var file = nodePath.resolve(cwd, files[i]);
282
283 var stat = fs.statSync(file);
284
285 if (stat.isDirectory()) {
286 doWalk(file);
287 } else {
288 fileCallback(file, context);
289 }
290 }
291}
292
293if (args.clean) {
294 var deleteCount = 0;
295
296 walk(
297 args.files,
298 {
299 file: function(file, context) {
300 var basename = nodePath.basename(file);
301
302 if (
303 basename.endsWith(".marko.js") ||
304 basename.endsWith(".marko.html") ||
305 basename.endsWith(".marko.xml.js")
306 ) {
307 context.beginAsync();
308 fs.unlink(file, function(err) {
309 if (err) {
310 return context.endAsync(err);
311 }
312 deleteCount++;
313 console.log("Deleted: " + file);
314 context.endAsync();
315 });
316 }
317 }
318 },
319 function() {
320 if (deleteCount === 0) {
321 console.log("No *.marko.js files were found. Already clean.");
322 } else {
323 console.log("Deleted " + deleteCount + " file(s)");
324 }
325 }
326 );
327} else {
328 var found = {};
329 var compileCount = 0;
330 var failed = [];
331
332 var compile = function(path, context) {
333 if (found[path]) {
334 return;
335 }
336
337 found[path] = true;
338 var outPath = path + ".js";
339
340 if (!args.quiet)
341 console.log(
342 "Compiling:\n Input: " +
343 relPath(path) +
344 "\n Output: " +
345 relPath(outPath) +
346 "\n"
347 );
348
349 context.beginAsync();
350
351 markoCompiler.compileFile(path, compileOptions, function(err, src) {
352 if (err) {
353 failed.push(
354 'Failed to compile "' +
355 relPath(path) +
356 '". Error: ' +
357 (err.stack || err)
358 );
359 context.endAsync(err);
360 return;
361 }
362
363 context.beginAsync();
364 fs.writeFile(outPath, src, { encoding: "utf8" }, function(err) {
365 if (err) {
366 failed.push(
367 'Failed to write "' + path + '". Error: ' + (err.stack || err)
368 );
369 context.endAsync(err);
370 return;
371 }
372
373 compileCount++;
374 context.endAsync();
375 });
376
377 context.endAsync();
378 });
379 };
380
381 if (args.files && args.files.length) {
382 walk(
383 args.files,
384 {
385 file: function(file, context) {
386 var basename = nodePath.basename(file);
387
388 if (
389 basename.endsWith(".marko") ||
390 basename.endsWith(".marko.html") ||
391 basename.endsWith(".marko.xml")
392 ) {
393 compile(file, context);
394 }
395 }
396 },
397 function(err) {
398 if (err) {
399 if (failed.length) {
400 console.error(
401 "The following errors occurred:\n- " + failed.join("\n- ")
402 );
403 } else {
404 console.error(err);
405 }
406
407 return;
408 }
409
410 if (compileCount === 0) {
411 console.log("No templates found");
412 } else {
413 console.log("Compiled " + compileCount + " templates(s)");
414 }
415 }
416 );
417 }
418}