1 | var path = require('path');
|
2 | var mkdirp = require('mkdirp');
|
3 | var fs = require('fs');
|
4 | var Promise = require('bluebird');
|
5 | var asp = require('bluebird').promisify;
|
6 | var extend = require('./utils').extend;
|
7 |
|
8 | var fromFileURL = require('./utils').fromFileURL;
|
9 | var toFileURL = require('./utils').toFileURL;
|
10 |
|
11 | function countLines(str) {
|
12 | return str.split(/\r\n|\r|\n/).length;
|
13 | }
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | function processOutputs(outputs) {
|
23 | var removeSourceMaps = require('./sourcemaps').removeSourceMaps;
|
24 |
|
25 | var offset = 0;
|
26 |
|
27 | var outputObj = {};
|
28 |
|
29 | var sources = outputObj.sources = [];
|
30 | var sourceMapsWithOffsets = outputObj.sourceMapsWithOffsets = [];
|
31 |
|
32 | outputs.forEach(function(output) {
|
33 | var source;
|
34 | if (typeof output == 'object') {
|
35 | source = output.source || '';
|
36 | var offset_ = output.sourceMapOffset || 0;
|
37 | var map = output.sourceMap;
|
38 | if (map) {
|
39 | sourceMapsWithOffsets.push([offset + offset_, map]);
|
40 | }
|
41 | }
|
42 |
|
43 | else if (typeof output == 'string') {
|
44 | source = output;
|
45 | }
|
46 | else {
|
47 | var format = output ? output.toString() : typeof output;
|
48 | throw new Error("Unexpected output format: " + format);
|
49 | }
|
50 | source = removeSourceMaps(source || '');
|
51 | offset += countLines(source);
|
52 | sources.push(source);
|
53 | });
|
54 |
|
55 | return outputObj;
|
56 | }
|
57 |
|
58 | function createOutput(outFile, outputs, basePath, sourceMaps, sourceMapContents) {
|
59 | var concatenateSourceMaps = require('./sourcemaps').concatenateSourceMaps;
|
60 |
|
61 | var outputObj = processOutputs(outputs);
|
62 |
|
63 | if (sourceMaps)
|
64 | var sourceMap = concatenateSourceMaps(outFile, outputObj.sourceMapsWithOffsets, basePath, sourceMapContents);
|
65 |
|
66 | var output = outputObj.sources.join('\n');
|
67 |
|
68 | return {
|
69 | source: output,
|
70 | sourceMap: sourceMap
|
71 | };
|
72 | }
|
73 |
|
74 | function minify(output, fileName, mangle, uglifyOpts) {
|
75 | var uglify = require('uglify-js');
|
76 | var ast;
|
77 | try{
|
78 | ast = uglify.parse(output.source, { filename: fileName });
|
79 | } catch(e){
|
80 | throw new Error(e);
|
81 | }
|
82 | ast.figure_out_scope();
|
83 |
|
84 | ast = ast.transform(uglify.Compressor(uglifyOpts.compress));
|
85 | ast.figure_out_scope();
|
86 | if (mangle !== false)
|
87 | ast.mangle_names();
|
88 |
|
89 | var sourceMap;
|
90 | if (output.sourceMap) {
|
91 | if (typeof output.sourceMap === 'string')
|
92 | output.sourceMap = JSON.parse(output.sourceMap);
|
93 |
|
94 | var sourceMapIn = output.sourceMap;
|
95 | sourceMap = uglify.SourceMap({
|
96 | file: fileName,
|
97 | orig: sourceMapIn
|
98 | });
|
99 |
|
100 | if (uglifyOpts.sourceMapIncludeSources && sourceMapIn && Array.isArray(sourceMapIn.sourcesContent)) {
|
101 | sourceMapIn.sourcesContent.forEach(function(content, idx) {
|
102 | sourceMap.get().setSourceContent(sourceMapIn.sources[idx], content);
|
103 | });
|
104 | }
|
105 | }
|
106 |
|
107 | var outputOptions = uglifyOpts.beautify;
|
108 |
|
109 | outputOptions.comments = outputOptions.comments || function(node, comment) {
|
110 | return comment.line === 1 && comment.col === 0;
|
111 | };
|
112 | outputOptions.source_map = sourceMap;
|
113 |
|
114 | output.source = ast.print_to_string(outputOptions);
|
115 | output.sourceMap = sourceMap;
|
116 |
|
117 | return output;
|
118 | }
|
119 |
|
120 | function writeOutputFile(outFile, source, sourceMap) {
|
121 | var outDir = path.dirname(outFile);
|
122 |
|
123 | return asp(mkdirp)(path.dirname(outFile))
|
124 | .then(function() {
|
125 | if (!sourceMap)
|
126 | return;
|
127 |
|
128 | var sourceMapFileName = path.basename(outFile) + '.map';
|
129 | source += '\n//# sourceMappingURL=' + sourceMapFileName;
|
130 |
|
131 | return asp(fs.writeFile)(path.resolve(outDir, sourceMapFileName), sourceMap);
|
132 | })
|
133 | .then(function() {
|
134 | return asp(fs.writeFile)(outFile, source);
|
135 | });
|
136 | }
|
137 |
|
138 | exports.inlineSourceMap = inlineSourceMap;
|
139 | function inlineSourceMap(source, sourceMap) {
|
140 | if (!sourceMap)
|
141 | throw new Error('NOTHING TO INLINE');
|
142 | return source + '\n//# sourceMappingURL=data:application/json;base64,'
|
143 | + new Buffer(sourceMap.toString()).toString('base64');
|
144 | }
|
145 |
|
146 | exports.writeOutputs = function(outputs, baseURL, outputOpts) {
|
147 | var outFile = outputOpts.outFile && path.resolve(outputOpts.outFile);
|
148 | var basePath = fromFileURL(baseURL);
|
149 | var fileName = outFile && path.basename(outFile) || 'output.js';
|
150 |
|
151 | var output = createOutput(outFile || path.resolve(basePath, fileName), outputs, basePath, outputOpts.sourceMaps, outputOpts.sourceMapContents);
|
152 |
|
153 | if (outputOpts.minify)
|
154 | output = minify(output, fileName, outputOpts.mangle, outputOpts.uglify);
|
155 |
|
156 | if (outputOpts.sourceMaps == 'inline') {
|
157 | output.source = inlineSourceMap(output.source, output.sourceMap);
|
158 | output.sourceMap = undefined;
|
159 | }
|
160 |
|
161 | if (!outputOpts.outFile)
|
162 | return Promise.resolve(output);
|
163 |
|
164 | return writeOutputFile(outFile, output.source, output.sourceMap).then(function() {
|
165 | return output;
|
166 | });
|
167 | };
|