1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | var undef, conf
|
17 | , PAC = require("../../package.json")
|
18 | , fs = require("fs")
|
19 | , child = require("child_process")
|
20 | , spawn = child.spawn
|
21 | , now = new Date()
|
22 | , path = require("../path")
|
23 | , events = require("../events")
|
24 | , cli = require("./")
|
25 | , Fn = require("../fn.js").Fn
|
26 | , files = {}
|
27 | , fileHashes = {}
|
28 | , hasOwn = files.hasOwnProperty
|
29 | , adapters = File.adapters = {
|
30 | css: {
|
31 | split: cssSplit, min: cssMin, banner: "/*! {0} */\n"
|
32 | },
|
33 | html: {
|
34 | split: htmlSplit, sep: "", banner: "<!-- {0} -->\n",
|
35 | transpilers: {
|
36 | js: jsToHtml, css: cssToHtml
|
37 | }
|
38 | },
|
39 | js: {
|
40 | min: jsMin, banner: "/*! {0} */\n",
|
41 | transpilers: {
|
42 | tpl: tplToJs, view: tplToJs
|
43 | }
|
44 | },
|
45 | tpl: {
|
46 | min: tplMin, banner: "/{0}\n"
|
47 | }
|
48 | }
|
49 | , translate = {
|
50 |
|
51 | stability: "0 - Deprecated,1 - Experimental,2 - Unstable,3 - Stable,4 - API Frozen,5 - Locked".split(","),
|
52 | date: now.toISOString().split("T")[0]
|
53 | }
|
54 | , linked = __dirname.indexOf(process.cwd()) !== 0
|
55 |
|
56 | adapters.view = adapters.tpl
|
57 |
|
58 | try {
|
59 | conf = require(path.resolve("package.json"))
|
60 | console.log("# Build %s@%s\n%s %s", conf.name, conf.version, PAC.name, PAC.version)
|
61 | child.spawnSync("uglifyjs", ["--version"], {stdio: "inherit"})
|
62 | } catch(e) {
|
63 | console.error(e)
|
64 | conf = {}
|
65 | }
|
66 |
|
67 | if (linked) {
|
68 | module.paths = require("module")._nodeModulePaths(process.cwd())
|
69 |
|
70 |
|
71 | }
|
72 |
|
73 | function File(_name, _opts) {
|
74 | var file = this
|
75 | , name = _name === "-" ? _name : path.resolve(_name.split("?")[0])
|
76 |
|
77 | if (_name && files[name]) {
|
78 | return files[name]
|
79 | }
|
80 | if (!(file instanceof File)) {
|
81 | return new File(_name, _opts)
|
82 | }
|
83 |
|
84 | var opts = file.opts = _opts || {}
|
85 | , ext = file.ext = opts.ext || (
|
86 | name === "-" ?
|
87 | "" + opts.input :
|
88 | name
|
89 | ).split(".").pop()
|
90 |
|
91 | files[name] = file
|
92 | file._depends = []
|
93 | file.write = file.write.bind(file)
|
94 |
|
95 | if (!("root" in opts)) {
|
96 | opts.root = name.replace(/[^\/]*$/, "")
|
97 | }
|
98 | file.name = opts.name = name
|
99 |
|
100 | if (typeof opts.input == "string") {
|
101 | opts.input = [ opts.input ]
|
102 | }
|
103 | if (!opts.warnings) opts.warnings = []
|
104 |
|
105 | if (opts.sourceMap === true) {
|
106 | opts.sourceMap = name.replace(/\?|$/, ".map$&").slice(opts.root.length)
|
107 | }
|
108 | if (opts.drop) {
|
109 | if (!opts.replace) {
|
110 | opts.replace = []
|
111 | }
|
112 | opts.replace.push(
|
113 | [ new RegExp("\\/\\/(?=\\*\\*\\s+(?:" + opts.drop.replace(/[\s,]+/g, "|") + "))", "g"), "/"],
|
114 | [ new RegExp("\\/(\\*{2,})\\s+(?:" + opts.drop.replace(/[^\w]+/g, "|") + ")\\s+\\1\\/", "g"), "$&/*"]
|
115 | )
|
116 | }
|
117 |
|
118 | file.reset()
|
119 |
|
120 | setImmediate(file.wait())
|
121 |
|
122 | file.build()
|
123 |
|
124 | return file
|
125 | }
|
126 |
|
127 | File.prototype = {
|
128 | wait: Fn.hold,
|
129 | syncMethods: ["on", "toString"],
|
130 | depends: function(child) {
|
131 | var file = this
|
132 | child.on("change", file.write)
|
133 | },
|
134 | reset: function() {
|
135 | var file = this
|
136 |
|
137 | file._depends.forEach(function() {
|
138 | child.off("change", file.write)
|
139 | })
|
140 | file._depends.length = 0
|
141 | file.content = []
|
142 | },
|
143 | build: function() {
|
144 | var file = this
|
145 | , opts = file.opts
|
146 | , resume = file.wait()
|
147 | , adapter = adapters[file.ext] || {}
|
148 | , buildResume = Fn.wait(min)
|
149 |
|
150 | if (opts.input) {
|
151 | file.content = opts.input.map(function(fileName, i, arr) {
|
152 | var child = fileName
|
153 | if (!(fileName instanceof File)) {
|
154 | if (!fs.existsSync(path.resolve(fileName))) {
|
155 | fileName = arr[i] = require.resolve(fileName)
|
156 | }
|
157 | child = File(fileName, {
|
158 | root: opts.root,
|
159 | warnings: opts.warnings
|
160 | })
|
161 | }
|
162 | child.then(buildResume.wait())
|
163 | file.depends(child)
|
164 | return child
|
165 | })
|
166 | file.write()
|
167 | } else {
|
168 | if (!opts.mem) {
|
169 | var source = cli.readFile(file.name)
|
170 | file.content = adapter.split ? adapter.split(source, opts) : [ source ]
|
171 | }
|
172 | file.content.forEach(function(junk, i, arr) {
|
173 | if (junk instanceof File) {
|
174 | file.depends(junk)
|
175 | junk.then(buildResume.wait())
|
176 | }
|
177 | })
|
178 | }
|
179 |
|
180 | setImmediate(buildResume)
|
181 |
|
182 | function min() {
|
183 | file.src = file.content
|
184 | .filter(Boolean)
|
185 | .map(function(f) {
|
186 | if (
|
187 | typeof f === "string" ||
|
188 | adapters[file.ext] === adapters[f.ext] ||
|
189 | !adapters[file.ext].transpilers ||
|
190 | !adapters[file.ext].transpilers[f.ext]
|
191 | ) return f
|
192 | return adapters[file.ext].transpilers[f.ext](f.toString())
|
193 | })
|
194 | .join("sep" in adapter ? adapter.sep : "\n")
|
195 |
|
196 | if (opts.replace) {
|
197 | opts.replace.forEach(function(arr) {
|
198 | file.src = file.src.replace(arr[0], arr[1] || "")
|
199 | })
|
200 | }
|
201 |
|
202 | if (adapter.min && opts.min) {
|
203 | var map = file.content.reduce(function(map, f) {
|
204 | var str = f instanceof File ? f.src : f
|
205 | if (opts.replace) opts.replace.forEach(function(arr) {
|
206 | str = str.replace(arr[0], arr[1])
|
207 | })
|
208 | map[f.name] = (
|
209 | typeof f !== "string" &&
|
210 | adapters[file.ext] !== adapters[f.ext] &&
|
211 | adapters[file.ext].transpilers &&
|
212 | adapters[file.ext].transpilers[f.ext] ?
|
213 | adapters[file.ext].transpilers[f.ext](f.toString()) :
|
214 | str
|
215 | )
|
216 | return map
|
217 | }, {})
|
218 | adapter.min(map, opts, function(err, res) {
|
219 | file.min = res
|
220 | resume()
|
221 | })
|
222 | } else {
|
223 | resume()
|
224 | }
|
225 | }
|
226 | },
|
227 | write: function(by) {
|
228 | var file = this
|
229 | if (file.name === "-") {
|
230 | process.stdout.write(file.toString())
|
231 | } else if (!file.opts.mem) {
|
232 | cli.writeFile(file.name, file.toString())
|
233 | if (fileHashes[file.name]) {
|
234 |
|
235 | var fullHash = child.execSync("git hash-object " + file.name).toString("utf8")
|
236 | fileHashes[file.name] = child.execSync("git rev-parse --short=4 " + fullHash).toString("utf8")
|
237 | }
|
238 | }
|
239 | if (file.opts.warnings.length) {
|
240 | console.error("WARNINGS:\n - " + file.opts.warnings.join("\n - "))
|
241 | }
|
242 | },
|
243 | then: function(next, scope) {
|
244 | if (typeof next == "function") {
|
245 | next.call(scope || this)
|
246 | }
|
247 | return this
|
248 | },
|
249 | toString: function() {
|
250 | var file = this
|
251 | , opts = file.opts
|
252 | , adapter = adapters[file.ext] || {}
|
253 | , banner = opts.banner && adapter.banner && adapter.banner.replace(/\{0\}/g, opts.banner)
|
254 | , str = adapter.min && opts.min ? file.min : format(file.src)
|
255 | , out = (
|
256 | (banner ? format(banner) : "") +
|
257 | str.trim() +
|
258 | (opts.sourceMap ? "\n//# sourceMappingURL=" + opts.sourceMap + "\n" : "")
|
259 | )
|
260 |
|
261 | if (opts.outPrefix) {
|
262 | out = opts.outPrefix + out.split("\n").join("\n" + opts.outPrefix)
|
263 | }
|
264 |
|
265 | return out
|
266 | }
|
267 | }
|
268 |
|
269 | events.asEmitter(File.prototype)
|
270 |
|
271 | function defMap(str) {
|
272 | var chr = str.charAt(0)
|
273 | , slice = str.slice(1)
|
274 | return chr == "+" ? lastStr + slice :
|
275 | chr == "%" ? ((chr = lastStr.lastIndexOf(slice.charAt(0))), (chr > 0 ? lastStr.slice(0, chr) : lastStr)) + slice :
|
276 | (chr == "." && this.root ? this.root : "") + (lastStr = str)
|
277 | }
|
278 |
|
279 | function htmlQuote(val) {
|
280 |
|
281 |
|
282 | return (
|
283 | /^[^\s'"`<>=]+$/.test(val) ? '"' + val + '"' :
|
284 | val
|
285 | )
|
286 | }
|
287 |
|
288 | function htmlSplit(str, opts) {
|
289 | var newOpts, pos, file, ext, file2, match, match2, match3, out, min, replace, tmp, haveInlineJS
|
290 | , mined = []
|
291 | , lastIndex = 0
|
292 | , re = /<link[^>]+href="([^"]*?)"[^>]*?>|<(script)[^>]+src="([^>]*?)"[^>]*><\/\2>/ig
|
293 | , banner, bannerRe = /\sbanner=(("|')([^]+?)\2|[^\s]+)/i
|
294 | , inline, inlineRe = /\sinline\b/i
|
295 | , drop, dropRe = /\sdrop=(("|')([^]*?)\2|[^\s]+)/i
|
296 | , minRe = /\smin\b(?:=["']?(.+?)["'])?/i
|
297 | , requireRe = /\srequire=(("|')([^]*?)\2|[^\s]+)/i
|
298 | , excludeRe = /\sexclude\b/i
|
299 | , loadFiles = []
|
300 | , hashes = {}
|
301 |
|
302 | str = str
|
303 | .replace(/<!--(?!\[if)[^]*?-->/g, "")
|
304 |
|
305 | for (out = [ str ]; match = re.exec(str); ) {
|
306 | file = opts.root + (match[1] || match[3])
|
307 | ext = file.split(".").pop()
|
308 | pos = out.length
|
309 | out.splice(-1, 1,
|
310 | str.slice(lastIndex, match.index), "",
|
311 | str.slice(lastIndex = re.lastIndex)
|
312 | )
|
313 |
|
314 | banner = bannerRe.exec(match[0])
|
315 | inline = inlineRe.test(match[0])
|
316 | drop = dropRe.exec(match[0])
|
317 |
|
318 | if (match2 = requireRe.exec(match[0])) {
|
319 | lastStr = opts.root
|
320 | tmp = (match2[2] ? match2[3] : match2[1]).match(/[^,\s]+/g)
|
321 | match2 = File(file, {
|
322 | input: tmp ? tmp.map(defMap, opts) : [],
|
323 | drop: drop ? drop[3] || drop[1] : ""
|
324 | })
|
325 | if (!tmp) {
|
326 | match2._requireNext = true
|
327 | }
|
328 | }
|
329 |
|
330 | if (excludeRe.test(match[0])) {
|
331 | continue
|
332 | }
|
333 |
|
334 | newOpts = {
|
335 | min: 1,
|
336 | replace: inline && [
|
337 | ["/*!{loadFiles}*/", loadFiles],
|
338 | ["/*!{loadHashes}*/", JSON.stringify(hashes).slice(1, -1)]
|
339 | ],
|
340 | banner: banner ? banner[3] || banner[1] : "",
|
341 | drop: drop ? drop[3] || drop[1] : ""
|
342 | }
|
343 |
|
344 | if (match3 = minRe.exec(match[0])) {
|
345 | lastStr = file.slice(opts.root.length)
|
346 | file2 = (
|
347 | match3[1] ? path.resolve(opts.root, defMap.call(opts, match3[1])) :
|
348 | min && (
|
349 | adapters[min.ext] === adapters[ext] ||
|
350 | (adapters[min.ext].transpilers||[])[ext]
|
351 | ) ? min.name :
|
352 | opts.root + mined.length.toString(32) + "." + ext
|
353 | )
|
354 | if (!min || min.name !== file2) {
|
355 | newOpts.input = []
|
356 | min = File(file2, newOpts)
|
357 | mined.push(min.wait())
|
358 | }
|
359 | min.opts.input.push(match2 || file.replace(/\?.*/, ""))
|
360 | if (match2 && match2._requireNext) {
|
361 | min = match2
|
362 | }
|
363 | if (min.isLoaded) {
|
364 | continue
|
365 | }
|
366 | min.isLoaded = 1
|
367 | file = file2
|
368 | }
|
369 | var dataIf = /\sif="([^"?]+)/.exec(match[0])
|
370 | if (inline) {
|
371 | if (match2 && !match3) {
|
372 | newOpts.input = [match2]
|
373 | newOpts.mem = true
|
374 | file = "mem:" + file
|
375 | }
|
376 | tmp = File(file, newOpts)
|
377 | if (match[2]) haveInlineJS = true
|
378 | mined.push(tmp.wait())
|
379 | out[pos] = tmp
|
380 | } else if ((haveInlineJS && match[2]) || dataIf) {
|
381 | loadFiles.push(
|
382 | (dataIf ? "(" + dataIf[1] + ")&&'" : "'") +
|
383 | replacePath(path.relative(opts.root, file), opts) + "'"
|
384 | )
|
385 | } else {
|
386 | tmp = match[0]
|
387 | if (match3) {
|
388 | tmp = tmp
|
389 | .replace(minRe, "")
|
390 | .replace(requireRe, "")
|
391 | .replace(bannerRe, "")
|
392 | .replace(match[1] || match[3], path.relative(opts.root, file))
|
393 | }
|
394 | out[pos] = tmp
|
395 | }
|
396 | }
|
397 | mined.forEach(function(fn) { fn() })
|
398 | return out.filter(Boolean).map(htmlMin, opts)
|
399 | }
|
400 |
|
401 | function htmlMin(str) {
|
402 | var opts = this
|
403 | return typeof str !== "string" ? str : str
|
404 | .replace(/[\r\n][\r\n\s]*[\r\n]/g, "\n")
|
405 | .replace(/\t/g, " ")
|
406 | .replace(/\s+(?=<|\/?>|$)/g, "")
|
407 | .replace(/\b(href|src)="(?!data:)(.+?)"/gi, function(_, tag, file) {
|
408 | return tag + '="' + replacePath(file, opts) + '"'
|
409 | })
|
410 | }
|
411 |
|
412 | function jsToHtml(str) {
|
413 | return '<script>' + str + '</script>'
|
414 |
|
415 | }
|
416 | function cssToHtml(str) {
|
417 | return '<style>' + str + '</style>'
|
418 | }
|
419 |
|
420 | function cssSplit(str, opts) {
|
421 | var match, out
|
422 | , lastIndex = 0
|
423 | , re = /@import\s+url\((['"]?)(?!data:)(.+?)\1\);*/ig
|
424 |
|
425 | if (opts.root !== opts.name.replace(/[^\/]*$/, "")) {
|
426 | str = str.replace(/\/\*(?!!)[^]*?\*\/|url\((['"]?)(?!data:)(.+?)\1\)/ig, function(_, q, name) {
|
427 | return name ?
|
428 | 'url("' + replacePath(path.relative(opts.root, path.resolve(opts.name.replace(/[^\/]*$/, name))), opts) + '")' :
|
429 | _
|
430 | })
|
431 | }
|
432 |
|
433 | for (out = [ str ]; match = re.exec(str); ) {
|
434 | out.splice(-1, 1,
|
435 | str.slice(lastIndex, match.index),
|
436 | File(path.resolve(opts.root, match[2]), opts),
|
437 | str.slice(lastIndex = re.lastIndex)
|
438 | )
|
439 | }
|
440 | return out.filter(Boolean)
|
441 | }
|
442 |
|
443 | function cssMin(map, opts, next) {
|
444 | var name
|
445 | , out = ""
|
446 | for (name in map) if (hasOwn.call(map, name)) {
|
447 | out += typeof map[name] !== "string" ? map[name] : map[name]
|
448 | .replace(/\/\*(?!!)[^]*?\*\//g, "")
|
449 | .replace(/[\r\n]+/g, "\n")
|
450 |
|
451 | .replace(/(.*)\/\*!\s*([\w-]+)\s*([\w-.]*)\s*\*\//g, function(_, line, cmd, param) {
|
452 | switch (cmd) {
|
453 | case "data-uri":
|
454 | line = line.replace(/url\((['"]?)(.+?)\1\)/g, function(_, quote, fileName) {
|
455 | var str = fs.readFileSync(path.resolve(opts.root + fileName), "base64")
|
456 | return 'url("data:image/' + fileName.split(".").pop() + ";base64," + str + '")'
|
457 | })
|
458 | break;
|
459 | }
|
460 | return line
|
461 | })
|
462 |
|
463 |
|
464 | .replace(/(["'])((?:\\?.)*?)\1|[^"']+/g, function(_, q, str) {
|
465 | if (q) return q == "'" && str.indexOf('"') == -1 ? '"' + str + '"' : _
|
466 | return _.replace(/[\t\n]/g, " ")
|
467 | .replace(/ *([,;{}>~+]) */g, "$1")
|
468 | .replace(/^ +|;(?=})/g, "")
|
469 | .replace(/: +/g, ":")
|
470 | .replace(/ and\(/g, " and (")
|
471 | .replace(/}(?!})/g, "}\n")
|
472 | })
|
473 |
|
474 |
|
475 |
|
476 |
|
477 | .replace(/url\("([\w\/_.-]*)"\)/g, "url($1)")
|
478 | .replace(/([ :,])0\.([0-9]+)/g, "$1.$2")
|
479 | }
|
480 | next(null, out)
|
481 | }
|
482 |
|
483 | var npmChild
|
484 |
|
485 | function jsMin(map, opts, next) {
|
486 | if (!cli.command("uglifyjs")) {
|
487 | console.error("Error: uglify-js not found, run: npm i -g uglify-js\n")
|
488 | process.exit(1)
|
489 | }
|
490 | var name
|
491 | , result = ""
|
492 | , ps = spawn("uglifyjs", [
|
493 | "--warn",
|
494 | "--ie8",
|
495 | "--compress", "evaluate=false,properties=false",
|
496 | "--mangle", "eval",
|
497 | "--comments", "/^[@!]/",
|
498 | "--beautify", "beautify=false,semicolons=false,keep_quoted_props=true"
|
499 | ])
|
500 |
|
501 | ps.stderr.on("data", function onError(data) {
|
502 | data = data.toString().trim()
|
503 | if (data !== "") opts.warnings.push(data)
|
504 | })
|
505 | ps.stdout.on("data", function(data) {
|
506 | result += data.toString()
|
507 | })
|
508 | ps.on("close", function(code) {
|
509 | if (code !== 0) {
|
510 | console.error(opts.warnings)
|
511 | throw Error("uglifyjs exited with " + code)
|
512 | }
|
513 | result = result
|
514 | .replace(/\/\*!cc_on\*\//g, "/*@cc_on")
|
515 | .replace(/\/\*!cc_off\*\//g, "@*/")
|
516 | next(null, result)
|
517 | })
|
518 | for (name in map) if (hasOwn.call(map, name)) {
|
519 | ps.stdin.write(map[name])
|
520 | }
|
521 | ps.stdin.end()
|
522 | }
|
523 |
|
524 | function tplMin(map, opts, next) {
|
525 | var out = Object.keys(map)
|
526 | , pos = 0
|
527 |
|
528 | min()
|
529 |
|
530 | function min() {
|
531 | var i = pos++
|
532 | if (i < out.length) {
|
533 | _tplSplit(map[out[i]], opts, function(err, str) {
|
534 | out[i] = str
|
535 | min()
|
536 | })
|
537 | } else {
|
538 | next(null, out.join("\n"))
|
539 | }
|
540 | }
|
541 | }
|
542 |
|
543 | function _tplSplit(str, opts, next) {
|
544 | var templateRe = /^([ \t]*)(%?)((?:("|')(?:\\?.)*?\4|[-\w:.#[\]=])*)[ \t]*(([\])}]?).*?([[({]?))$/gm
|
545 | , out = [""]
|
546 | , parent = 0
|
547 | , stack = [-1]
|
548 | , resume = Fn.wait(function() {
|
549 | next(null, out.join("\n"))
|
550 | })
|
551 |
|
552 | str.replace(templateRe, work)
|
553 |
|
554 | resume()
|
555 |
|
556 | function work(all, indent, plugin, name, q, text, mapEnd, mapStart, offset) {
|
557 | if (offset && all === indent) return
|
558 |
|
559 | for (q = indent.length; q <= stack[0]; ) {
|
560 | if (typeof out[parent] !== "string") {
|
561 | parent = out.push("") - 1
|
562 | }
|
563 | stack.shift()
|
564 | }
|
565 |
|
566 | if (typeof out[parent] !== "string") {
|
567 | if (!out[parent].content.length) out[parent].content.push(all)
|
568 | else out[parent].content[0] += all + "\n"
|
569 | } else if (plugin && (name === "js" || name === "css")) {
|
570 | out[parent] += all
|
571 | parent = out.push(
|
572 | File("", {mem:1, min:1, ext:name, outPrefix: indent + " "}).then(resume.wait())
|
573 | ) - 1
|
574 | stack.unshift(q)
|
575 | } else {
|
576 | if (text && text.charAt(0) === "/") return
|
577 | out[parent] += all + "\n"
|
578 | }
|
579 | }
|
580 | }
|
581 |
|
582 | function tplToJs(input) {
|
583 | var line
|
584 | , arr = input.split("\n")
|
585 | , i = 0
|
586 | , l = arr.length
|
587 | , map = {
|
588 | "%js": "",
|
589 | "%css": ""
|
590 | }
|
591 | , singles = 0
|
592 | , doubles = 0
|
593 | , last = -1
|
594 |
|
595 | for (; i < l; ) {
|
596 | line = arr[i++]
|
597 | if (line === "") {
|
598 | if (last > -1) {
|
599 | map[arr[last]] += arr.splice(last, i - last).slice(1).join("\n")
|
600 | i -= i - last
|
601 | last = -1
|
602 | }
|
603 | } else if (map.hasOwnProperty(line)) last = i - 1
|
604 | }
|
605 |
|
606 | if (map["%js"]) {
|
607 | map["%js"] = ";!function(){" + map["%js"] + "}()"
|
608 | }
|
609 | if (map["%css"]) {
|
610 | cssMin({a: map["%css"]}, {}, function(err, str) {
|
611 | map["%css"] = ";xhr.css('" + str.replace(/\n/g, "").replace(/'/g, "\\'") + "')"
|
612 | })
|
613 | }
|
614 |
|
615 | tplMin({a: arr.join("\n")}, null, function(err, str) {
|
616 | input = str.replace(/\n+/g, "\\n")
|
617 | })
|
618 |
|
619 | for (i = input.length; i--; ) {
|
620 | if (input.charCodeAt(i) === 34) doubles++
|
621 | else if (input.charCodeAt(i) === 39) singles++
|
622 | }
|
623 | return map["%js"] + map["%css"] + (
|
624 | singles > doubles ?
|
625 | ';El.tpl("' + input.replace(/"/g, '\\"') + '")' :
|
626 | ";El.tpl('" + input.replace(/'/g, "\\'") + "')"
|
627 | )
|
628 | }
|
629 |
|
630 | function readFileHashes(next) {
|
631 | var leftover = ""
|
632 | , cwd = process.cwd() + "/"
|
633 | , git = spawn("git", ["ls-files", "-sz", "--abbrev=1"])
|
634 |
|
635 | git.stdout.on("data", onData).on("end", onEnd)
|
636 | git.stderr.pipe(process.stderr)
|
637 |
|
638 | function onData(data) {
|
639 | var lines = (leftover + data).split("\0")
|
640 |
|
641 | leftover = lines.pop()
|
642 | lines.forEach(onLine)
|
643 | }
|
644 |
|
645 | function onEnd() {
|
646 | onLine(leftover)
|
647 | next()
|
648 | }
|
649 |
|
650 | function onLine(line) {
|
651 | if (line !== "") {
|
652 | fileHashes[cwd + line.slice(1 + line.indexOf("\t"))] = line.split(" ")[1]
|
653 | }
|
654 | }
|
655 |
|
656 |
|
657 |
|
658 |
|
659 |
|
660 | }
|
661 |
|
662 | function execute(args, i) {
|
663 | var arg, banner, input, output
|
664 |
|
665 | for (; arg = args[i++]; ) {
|
666 | switch (arg) {
|
667 | case "-b":
|
668 | case "--banner":
|
669 | banner = args[i++]
|
670 | break;
|
671 | case "-i":
|
672 | case "--input":
|
673 | if (!input) input = []
|
674 | input.push(args[i++])
|
675 | break;
|
676 | case "-o":
|
677 | case "--output":
|
678 | output = args[i++]
|
679 | break;
|
680 | case "-w":
|
681 | case "--worker":
|
682 | var opts = { warnings: [] }
|
683 | updateWorker(args[i++], opts, {})
|
684 | break;
|
685 | case "-r":
|
686 | case "--readme":
|
687 | updateReadme(args[i++])
|
688 | break;
|
689 | case "-v":
|
690 | case "--version":
|
691 | var opts = { warnings: [] }
|
692 | updateVersion(args[i++])
|
693 | break;
|
694 | default:
|
695 | if (arg.charAt(0) == "-") {
|
696 | args.splice.apply(
|
697 | args,
|
698 | [i, 0].concat(arg.replace(/\w(?!$)/g,"$& " + args[i] + " -").split(" "))
|
699 | )
|
700 | }
|
701 | }
|
702 | if (input && output) {
|
703 | File(output, {
|
704 | banner: banner,
|
705 | input: input,
|
706 | min: 1
|
707 | })
|
708 | banner = input = output = ""
|
709 | }
|
710 | }
|
711 | }
|
712 |
|
713 | if (module.parent) {
|
714 |
|
715 | exports.File = File
|
716 | exports.updateReadme = updateReadme
|
717 | exports.execute = function(args, i) {
|
718 | readFileHashes(function() {
|
719 | exports.execute = execute
|
720 | if (args.length > i) execute(args, i)
|
721 | else if (conf.litejs && Array.isArray(conf.litejs.build)) {
|
722 | conf.litejs.build.forEach(function(row) {
|
723 | execute(row.split(/\s+/), 0)
|
724 | })
|
725 | }
|
726 | })
|
727 | }
|
728 | }
|
729 |
|
730 | function replacePath(_p, opts) {
|
731 | var p = path.normalize(_p)
|
732 | if (p.indexOf("{hash}") > -1) {
|
733 | var full = path.resolve(opts.root, p.split("?")[0])
|
734 | p = p.replace(/{hash}/g, fileHashes[full] || +now)
|
735 | if (!fileHashes[full]) {
|
736 | opts.warnings.pushUniq("'" + full + "' not commited?")
|
737 | }
|
738 | }
|
739 | return p
|
740 | }
|
741 |
|
742 | function format(str) {
|
743 | return str.replace(/([\s\*\/]*@(version|date|author|stability)\s+).*/, function(all, match, tag) {
|
744 | tag = translate[tag] ? translate[tag][conf[tag]] || translate[tag] : conf[tag]
|
745 | return tag ? match + tag : all
|
746 | })
|
747 | }
|
748 |
|
749 | function updateReadme(file) {
|
750 | var current = cli.readFile(file)
|
751 | , updated = format(current)
|
752 |
|
753 | if (current != updated) {
|
754 | console.error("# Update readme: " + file)
|
755 | cli.writeFile(file, updated)
|
756 | }
|
757 | }
|
758 |
|
759 | function updateVersion(file) {
|
760 | var re = /(\s+VERSION\s*=\s*)("|').*?\2/
|
761 | , current = cli.readFile(file)
|
762 | , updated = current.replace(re, function(_, a, q) {
|
763 | return a + q + now.toISOString() + q
|
764 | })
|
765 | if (current !== updated) {
|
766 | console.error("# Update version: " + file)
|
767 | cli.writeFile(file, updated)
|
768 | }
|
769 | }
|
770 |
|
771 | function updateWorker(file, opts, hashes) {
|
772 | var root = file.replace(/[^\/]+$/, "")
|
773 | , re = /(\s+VERSION\s*=\s*)("|').*?\2/
|
774 | , current = cli.readFile(file)
|
775 | , updated = current
|
776 | .replace(re, function(_, a, q) {
|
777 | return a + q + now.toISOString() + q
|
778 | })
|
779 | .replace(/ FILES = (\[[^\]]+?\])/, function(all, files) {
|
780 | files = JSON.parse(files)
|
781 | .map(function(line) {
|
782 | var name = line.replace(/\?.*/, "")
|
783 | , full = path.resolve(root, name)
|
784 | if (!fileHashes[full]) {
|
785 | opts.warnings.pushUniq("'" + full + "' not commited?")
|
786 | } else if (name !== line) {
|
787 | hashes[name] = fileHashes[full]
|
788 | return name + "?" + fileHashes[full]
|
789 | }
|
790 | return line
|
791 | })
|
792 | return " FILES = " + JSON.stringify(files, null, "\t")
|
793 | })
|
794 |
|
795 | if (current != updated) {
|
796 | console.error("# Update worker: " + file)
|
797 | cli.writeFile(file, updated)
|
798 | }
|
799 | }
|
800 |
|
801 |
|