UNPKG

13.1 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8var _utils = require("@parcel/utils");
9
10var _logger = require("@parcel/logger");
11
12var _diagnostic = _interopRequireWildcard(require("@parcel/diagnostic"));
13
14var _stream = require("stream");
15
16var _nullthrows = _interopRequireDefault(require("nullthrows"));
17
18var _path = _interopRequireDefault(require("path"));
19
20var _url = _interopRequireDefault(require("url"));
21
22var _Bundle = require("./public/Bundle");
23
24var _BundleGraph = _interopRequireWildcard(require("./public/BundleGraph"));
25
26var _PluginOptions = _interopRequireDefault(require("./public/PluginOptions"));
27
28var _constants = require("./constants");
29
30function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
31
32function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
33
34function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
35
36function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
37
38class PackagerRunner {
39 constructor({
40 config,
41 farm,
42 options,
43 report
44 }) {
45 _defineProperty(this, "config", void 0);
46
47 _defineProperty(this, "options", void 0);
48
49 _defineProperty(this, "farm", void 0);
50
51 _defineProperty(this, "pluginOptions", void 0);
52
53 _defineProperty(this, "distDir", void 0);
54
55 _defineProperty(this, "distExists", void 0);
56
57 _defineProperty(this, "report", void 0);
58
59 _defineProperty(this, "writeBundleFromWorker", void 0);
60
61 this.config = config;
62 this.options = options;
63 this.pluginOptions = new _PluginOptions.default(this.options);
64 this.farm = farm;
65 this.report = report;
66 this.writeBundleFromWorker = farm ? farm.createHandle('runPackage') : () => {
67 throw new Error('Cannot call PackagerRunner.writeBundleFromWorker() in a worker');
68 };
69 }
70
71 async writeBundles(bundleGraph) {
72 let farm = (0, _nullthrows.default)(this.farm);
73 let {
74 ref,
75 dispose
76 } = await farm.createSharedReference(bundleGraph);
77 let promises = [];
78
79 for (let bundle of bundleGraph.getBundles()) {
80 // skip inline bundles, they will be processed via the parent bundle
81 if (bundle.isInline) {
82 continue;
83 }
84
85 promises.push(this.writeBundle(bundle, bundleGraph, ref).then(stats => {
86 bundle.stats = stats;
87 }));
88 }
89
90 await Promise.all(promises);
91 await dispose();
92 }
93
94 async writeBundle(bundle, bundleGraph, bundleGraphReference) {
95 let start = Date.now();
96 let cacheKey = await this.getCacheKey(bundle, bundleGraph);
97 let {
98 size
99 } = (await this.writeBundleFromCache({
100 bundle,
101 bundleGraph,
102 cacheKey
103 })) || (await this.writeBundleFromWorker({
104 bundle,
105 cacheKey,
106 bundleGraphReference,
107 options: this.options,
108 config: this.config
109 }));
110 return {
111 time: Date.now() - start,
112 size
113 };
114 }
115
116 async writeBundleFromCache({
117 bundle,
118 bundleGraph,
119 cacheKey
120 }) {
121 if (this.options.disableCache) {
122 return;
123 }
124
125 let cacheResult = await this.readFromCache(cacheKey);
126
127 if (cacheResult == null) {
128 return;
129 }
130
131 let {
132 contents,
133 map
134 } = cacheResult;
135 let {
136 size
137 } = await this.writeToDist({
138 bundle,
139 bundleGraph,
140 contents,
141 map
142 });
143 return {
144 size
145 };
146 }
147
148 async packageAndWriteBundle(bundle, bundleGraph, cacheKey) {
149 let start = Date.now();
150 let {
151 contents,
152 map
153 } = await this.getBundleResult(bundle, bundleGraph, cacheKey);
154 let {
155 size
156 } = await this.writeToDist({
157 bundle,
158 bundleGraph,
159 contents,
160 map
161 });
162 return {
163 time: Date.now() - start,
164 size
165 };
166 }
167
168 async getBundleResult(bundle, bundleGraph, cacheKey) {
169 let result;
170
171 if (!cacheKey && !this.options.disableCache) {
172 cacheKey = await this.getCacheKey(bundle, bundleGraph);
173 let cacheResult = await this.readFromCache(cacheKey);
174
175 if (cacheResult) {
176 // NOTE: Returning a new object for flow
177 return {
178 contents: cacheResult.contents,
179 map: cacheResult.map
180 };
181 }
182 }
183
184 let packaged = await this.package(bundle, bundleGraph);
185 let res = await this.optimize(bundle, bundleGraph, packaged.contents, packaged.map);
186 let map = res.map ? await this.generateSourceMap(bundle, res.map) : null;
187 result = {
188 contents: res.contents,
189 map
190 };
191
192 if (cacheKey != null) {
193 await this.writeToCache(cacheKey, result.contents, map);
194
195 if (result.contents instanceof _stream.Readable) {
196 return {
197 contents: this.options.cache.getStream(getContentKey(cacheKey)),
198 map: result.map
199 };
200 }
201 }
202
203 return result;
204 }
205
206 async package(internalBundle, bundleGraph) {
207 let bundle = new _Bundle.NamedBundle(internalBundle, bundleGraph, this.options);
208 this.report({
209 type: 'buildProgress',
210 phase: 'packaging',
211 bundle
212 });
213 let packager = await this.config.getPackager(bundle.filePath);
214
215 try {
216 return await packager.plugin.package({
217 bundle,
218 bundleGraph: new _BundleGraph.default(bundleGraph, this.options),
219 getSourceMapReference: map => {
220 return bundle.isInline || bundle.target.sourceMap && bundle.target.sourceMap.inline ? this.generateSourceMap((0, _Bundle.bundleToInternalBundle)(bundle), map) : _path.default.basename(bundle.filePath) + '.map';
221 },
222 options: this.pluginOptions,
223 logger: new _logger.PluginLogger({
224 origin: packager.name
225 }),
226 getInlineBundleContents: (bundle, bundleGraph) => {
227 if (!bundle.isInline) {
228 throw new Error('Bundle is not inline and unable to retrieve contents');
229 }
230
231 return this.getBundleResult((0, _Bundle.bundleToInternalBundle)(bundle), (0, _BundleGraph.bundleGraphToInternalBundleGraph)(bundleGraph));
232 }
233 });
234 } catch (e) {
235 throw new _diagnostic.default({
236 diagnostic: (0, _diagnostic.errorToDiagnostic)(e, packager.name)
237 });
238 }
239 }
240
241 async optimize(internalBundle, bundleGraph, contents, map) {
242 let bundle = new _Bundle.NamedBundle(internalBundle, bundleGraph, this.options);
243 let optimizers = await this.config.getOptimizers(bundle.filePath, internalBundle.pipeline);
244
245 if (!optimizers.length) {
246 return {
247 contents,
248 map
249 };
250 }
251
252 this.report({
253 type: 'buildProgress',
254 phase: 'optimizing',
255 bundle
256 });
257 let optimized = {
258 contents,
259 map
260 };
261
262 for (let optimizer of optimizers) {
263 try {
264 optimized = await optimizer.plugin.optimize({
265 bundle,
266 contents: optimized.contents,
267 map: optimized.map,
268 options: this.pluginOptions,
269 logger: new _logger.PluginLogger({
270 origin: optimizer.name
271 })
272 });
273 } catch (e) {
274 throw new _diagnostic.default({
275 diagnostic: (0, _diagnostic.errorToDiagnostic)(e, optimizer.name)
276 });
277 }
278 }
279
280 return optimized;
281 }
282
283 generateSourceMap(bundle, map) {
284 // sourceRoot should be a relative path between outDir and rootDir for node.js targets
285 let filePath = (0, _nullthrows.default)(bundle.filePath);
286
287 let sourceRoot = _path.default.relative(_path.default.dirname(filePath), this.options.projectRoot);
288
289 let inlineSources = false;
290
291 if (bundle.target) {
292 if (bundle.target.sourceMap && bundle.target.sourceMap.sourceRoot !== undefined) {
293 sourceRoot = bundle.target.sourceMap.sourceRoot;
294 } else if (bundle.target.env.context === 'browser' && this.options.mode !== 'production') {
295 sourceRoot = '/__parcel_source_root';
296 }
297
298 if (bundle.target.sourceMap && bundle.target.sourceMap.inlineSources !== undefined) {
299 inlineSources = bundle.target.sourceMap.inlineSources;
300 } else if (bundle.target.env.context !== 'node') {
301 // inlining should only happen in production for browser targets by default
302 inlineSources = this.options.mode === 'production';
303 }
304 }
305
306 return map.stringify({
307 file: _path.default.basename(filePath + '.map'),
308 fs: this.options.inputFS,
309 rootDir: this.options.projectRoot,
310 sourceRoot: !inlineSources ? _url.default.format(_url.default.parse(sourceRoot + '/')) : undefined,
311 inlineSources,
312 inlineMap: bundle.isInline || bundle.target.sourceMap && bundle.target.sourceMap.inline
313 });
314 }
315
316 getCacheKey(bundle, bundleGraph) {
317 let filePath = (0, _nullthrows.default)(bundle.filePath); // TODO: include packagers and optimizers used in inline bundles as well
318
319 let packager = this.config.getPackagerName(filePath);
320 let optimizers = this.config.getOptimizerNames(filePath);
321 let deps = Promise.all([packager, ...optimizers].map(async pkg => {
322 let {
323 pkg: resolvedPkg
324 } = await this.options.packageManager.resolve(`${pkg}/package.json`, `${this.config.filePath}/index`);
325 let version = (0, _nullthrows.default)(resolvedPkg).version;
326 return [pkg, version];
327 })); // TODO: add third party configs to the cache key
328
329 let {
330 minify,
331 scopeHoist,
332 sourceMaps
333 } = this.options;
334 return (0, _utils.md5FromObject)({
335 parcelVersion: _constants.PARCEL_VERSION,
336 deps,
337 opts: {
338 minify,
339 scopeHoist,
340 sourceMaps
341 },
342 hash: bundleGraph.getHash(bundle)
343 });
344 }
345
346 async readFromCache(cacheKey) {
347 let contentKey = getContentKey(cacheKey);
348 let mapKey = getMapKey(cacheKey);
349 let contentExists = await this.options.cache.blobExists(contentKey);
350
351 if (!contentExists) {
352 return null;
353 }
354
355 let mapExists = await this.options.cache.blobExists(mapKey);
356 return {
357 contents: this.options.cache.getStream(contentKey),
358 map: mapExists ? this.options.cache.getStream(mapKey) : null
359 };
360 }
361
362 async writeToDist({
363 bundle,
364 bundleGraph,
365 contents,
366 map
367 }) {
368 let {
369 inputFS,
370 outputFS
371 } = this.options;
372 let filePath = (0, _nullthrows.default)(bundle.filePath);
373
374 let dir = _path.default.dirname(filePath);
375
376 await outputFS.mkdirp(dir); // ? Got rid of dist exists, is this an expensive operation
377 // Use the file mode from the entry asset as the file mode for the bundle.
378 // Don't do this for browser builds, as the executable bit in particular is unnecessary.
379
380 let publicBundle = new _Bundle.NamedBundle(bundle, bundleGraph, this.options);
381 let writeOptions = publicBundle.env.isBrowser() ? undefined : {
382 mode: (await inputFS.stat((0, _nullthrows.default)(publicBundle.getMainEntry()).filePath)).mode
383 };
384 let size;
385
386 if (contents instanceof _stream.Readable) {
387 size = await writeFileStream(outputFS, filePath, contents, writeOptions);
388 } else {
389 await outputFS.writeFile(filePath, contents, writeOptions);
390 size = contents.length;
391 }
392
393 if (map != null) {
394 if (map instanceof _stream.Readable) {
395 await writeFileStream(outputFS, filePath + '.map', map);
396 } else {
397 await outputFS.writeFile(filePath + '.map', map);
398 }
399 }
400
401 return {
402 size
403 };
404 }
405
406 async writeToCache(cacheKey, contents, map) {
407 let contentKey = getContentKey(cacheKey);
408 await this.options.cache.setStream(contentKey, (0, _utils.blobToStream)(contents));
409
410 if (map != null) {
411 let mapKey = getMapKey(cacheKey);
412 await this.options.cache.setStream(mapKey, (0, _utils.blobToStream)(map));
413 }
414 }
415
416}
417
418exports.default = PackagerRunner;
419
420function writeFileStream(fs, filePath, stream, options) {
421 return new Promise((resolve, reject) => {
422 let fsStream = fs.createWriteStream(filePath, options);
423 stream.pipe(fsStream) // $FlowFixMe
424 .on('finish', () => resolve(fsStream.bytesWritten)).on('error', reject);
425 });
426}
427
428function getContentKey(cacheKey) {
429 return (0, _utils.md5FromString)(`${cacheKey}:content`);
430}
431
432function getMapKey(cacheKey) {
433 return (0, _utils.md5FromString)(`${cacheKey}:map`);
434}
\No newline at end of file