UNPKG

16.8 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8var _assert = _interopRequireDefault(require("assert"));
9
10var _path = _interopRequireDefault(require("path"));
11
12var _nullthrows = _interopRequireDefault(require("nullthrows"));
13
14var _utils = require("@parcel/utils");
15
16var _logger = require("@parcel/logger");
17
18var _diagnostic = _interopRequireWildcard(require("@parcel/diagnostic"));
19
20var _ConfigLoader = _interopRequireDefault(require("./ConfigLoader"));
21
22var _Dependency = require("./Dependency");
23
24var _ParcelConfig = _interopRequireDefault(require("./ParcelConfig"));
25
26var _ResolverRunner = _interopRequireDefault(require("./ResolverRunner"));
27
28var _Asset = require("./public/Asset");
29
30var _InternalAsset = _interopRequireWildcard(require("./InternalAsset"));
31
32var _summarizeRequest = _interopRequireDefault(require("./summarizeRequest"));
33
34var _PluginOptions = _interopRequireDefault(require("./public/PluginOptions"));
35
36var _constants = require("./constants");
37
38function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
39
40function _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; }
41
42function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
43
44function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
45
46function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
47
48function _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; }
49
50class Transformation {
51 constructor({
52 report,
53 request,
54 options,
55 workerApi
56 }) {
57 _defineProperty(this, "request", void 0);
58
59 _defineProperty(this, "configLoader", void 0);
60
61 _defineProperty(this, "configRequests", void 0);
62
63 _defineProperty(this, "options", void 0);
64
65 _defineProperty(this, "impactfulOptions", void 0);
66
67 _defineProperty(this, "workerApi", void 0);
68
69 _defineProperty(this, "parcelConfig", void 0);
70
71 _defineProperty(this, "report", void 0);
72
73 this.configRequests = [];
74 this.configLoader = new _ConfigLoader.default(options);
75 this.options = options;
76 this.report = report;
77 this.request = request;
78 this.workerApi = workerApi; // TODO: these options may not impact all transformations, let transformers decide if they care or not
79
80 let {
81 minify,
82 hot,
83 scopeHoist
84 } = this.options;
85 this.impactfulOptions = {
86 minify,
87 hot,
88 scopeHoist
89 };
90 }
91
92 async loadConfig(configRequest) {
93 let result = await this.configLoader.load(configRequest);
94 this.configRequests.push({
95 request: configRequest,
96 result
97 });
98 return result;
99 }
100
101 async run() {
102 this.report({
103 type: 'buildProgress',
104 phase: 'transforming',
105 filePath: this.request.filePath
106 });
107 let asset = await this.loadAsset();
108 let pipeline = await this.loadPipeline(this.request.filePath, asset.value.isSource, this.request.pipeline);
109 let results = await this.runPipelines(pipeline, asset);
110 let assets = results.map(a => a.value);
111
112 for (let {
113 request,
114 result
115 } of this.configRequests) {
116 let plugin = request.plugin != null && (await this.parcelConfig.loadPlugin(request.plugin));
117
118 if (plugin && plugin.preSerializeConfig) {
119 plugin.preSerializeConfig({
120 config: result
121 });
122 }
123 }
124
125 return {
126 assets,
127 configRequests: this.configRequests
128 };
129 }
130
131 async loadAsset() {
132 let {
133 filePath,
134 env,
135 code,
136 pipeline,
137 sideEffects
138 } = this.request;
139 let {
140 content,
141 size,
142 hash,
143 isSource
144 } = await (0, _summarizeRequest.default)(this.options.inputFS, this.request); // If the transformer request passed code rather than a filename,
145 // use a hash as the base for the id to ensure it is unique.
146
147 let idBase = code != null ? hash : filePath;
148 return new _InternalAsset.default({
149 idBase,
150 value: (0, _InternalAsset.createAsset)({
151 idBase,
152 filePath,
153 isSource,
154 type: _path.default.extname(filePath).slice(1),
155 hash,
156 pipeline,
157 env,
158 stats: {
159 time: 0,
160 size
161 },
162 sideEffects
163 }),
164 options: this.options,
165 content
166 });
167 }
168
169 async runPipelines(pipeline, initialAsset) {
170 var _ref;
171
172 let initialType = initialAsset.value.type;
173 let initialAssetCacheKey = this.getCacheKey([initialAsset], pipeline.configs);
174 let initialCacheEntry = await this.readFromCache(initialAssetCacheKey);
175 let assets = initialCacheEntry || (await this.runPipeline(pipeline, initialAsset));
176
177 if (!initialCacheEntry) {
178 await this.writeToCache(initialAssetCacheKey, assets, pipeline.configs);
179 }
180
181 let finalAssets = [];
182
183 for (let asset of assets) {
184 let nextPipeline;
185
186 if (asset.value.type !== initialType) {
187 nextPipeline = await this.loadNextPipeline({
188 filePath: initialAsset.value.filePath,
189 isSource: asset.value.isSource,
190 nextType: asset.value.type,
191 currentPipeline: pipeline
192 });
193 }
194
195 if (nextPipeline) {
196 let nextPipelineAssets = await this.runPipelines(nextPipeline, asset);
197 finalAssets = finalAssets.concat(nextPipelineAssets);
198 } else {
199 finalAssets.push(asset);
200 }
201 }
202
203 if (!pipeline.postProcess) {
204 return finalAssets;
205 }
206
207 let processedCacheEntry = await this.readFromCache(this.getCacheKey(finalAssets, pipeline.configs));
208 (0, _assert.default)(pipeline.postProcess != null);
209 let processedFinalAssets = (_ref = processedCacheEntry !== null && processedCacheEntry !== void 0 ? processedCacheEntry : await pipeline.postProcess(finalAssets)) !== null && _ref !== void 0 ? _ref : [];
210
211 if (!processedCacheEntry) {
212 await this.writeToCache(this.getCacheKey(processedFinalAssets, pipeline.configs), processedFinalAssets, pipeline.configs);
213 }
214
215 return processedFinalAssets;
216 }
217
218 async runPipeline(pipeline, initialAsset) {
219 let initialType = initialAsset.value.type;
220 let inputAssets = [initialAsset];
221 let resultingAssets;
222 let finalAssets = [];
223
224 for (let transformer of pipeline.transformers) {
225 resultingAssets = [];
226
227 for (let asset of inputAssets) {
228 if (asset.value.type !== initialType && (await this.loadNextPipeline({
229 filePath: initialAsset.value.filePath,
230 isSource: asset.value.isSource,
231 nextType: asset.value.type,
232 currentPipeline: pipeline
233 }))) {
234 finalAssets.push(asset);
235 continue;
236 }
237
238 try {
239 let transformerResults = await runTransformer(pipeline, asset, transformer.plugin, transformer.name, transformer.config);
240
241 for (let result of transformerResults) {
242 resultingAssets.push(asset.createChildAsset(result));
243 }
244 } catch (e) {
245 throw new _diagnostic.default({
246 diagnostic: (0, _diagnostic.errorToDiagnostic)(e, transformer.name)
247 });
248 }
249 }
250
251 inputAssets = resultingAssets;
252 }
253
254 finalAssets = finalAssets.concat(resultingAssets);
255 return Promise.all(finalAssets.map(asset => finalize((0, _nullthrows.default)(asset), (0, _nullthrows.default)(pipeline.generate))));
256 }
257
258 async readFromCache(cacheKey) {
259 if (this.options.disableCache || this.request.code != null) {
260 return null;
261 }
262
263 let cachedAssets = await this.options.cache.get(cacheKey);
264
265 if (!cachedAssets) {
266 return null;
267 }
268
269 return cachedAssets.map(value => new _InternalAsset.default({
270 value,
271 options: this.options
272 }));
273 }
274
275 async writeToCache(cacheKey, assets, configs) {
276 await Promise.all( // TODO: account for impactfulOptions maybe being different per pipeline
277 assets.map(asset => asset.commit((0, _utils.md5FromObject)({
278 impactfulOptions: this.impactfulOptions,
279 configs: getImpactfulConfigInfo(configs)
280 }))));
281 this.options.cache.set(cacheKey, assets.map(a => a.value));
282 }
283
284 getCacheKey(assets, configs) {
285 let assetsKeyInfo = assets.map(a => ({
286 filePath: a.value.filePath,
287 hash: a.value.hash
288 }));
289 return (0, _utils.md5FromObject)({
290 parcelVersion: _constants.PARCEL_VERSION,
291 assets: assetsKeyInfo,
292 configs: getImpactfulConfigInfo(configs),
293 env: this.request.env,
294 impactfulOptions: this.impactfulOptions
295 });
296 }
297
298 async loadPipeline(filePath, isSource, pipelineName) {
299 let configRequest = {
300 filePath,
301 env: this.request.env,
302 isSource,
303 pipeline: pipelineName,
304 meta: {
305 actionType: 'transformation'
306 }
307 };
308 let configs = new Map();
309 let config = await this.loadConfig(configRequest);
310 let result = (0, _nullthrows.default)(config.result);
311 let parcelConfig = new _ParcelConfig.default(config.result, this.options.packageManager); // A little hacky
312
313 this.parcelConfig = parcelConfig;
314 configs.set('parcel', config);
315
316 for (let [moduleName] of config.devDeps) {
317 let plugin = await parcelConfig.loadPlugin(moduleName); // TODO: implement loadPlugin in existing plugins that require config
318
319 if (plugin.loadConfig) {
320 let thirdPartyConfig = await this.loadTransformerConfig({
321 filePath,
322 plugin: moduleName,
323 parcelConfigPath: result.filePath,
324 isSource
325 });
326 configs.set(moduleName, thirdPartyConfig);
327 }
328 }
329
330 let transformers = await parcelConfig.getTransformers(filePath, pipelineName);
331 let pipeline = {
332 id: transformers.map(t => t.name).join(':'),
333 transformers: transformers.map(transformer => {
334 var _configs$get;
335
336 return {
337 name: transformer.name,
338 config: (_configs$get = configs.get(transformer.name)) === null || _configs$get === void 0 ? void 0 : _configs$get.result,
339 plugin: transformer.plugin
340 };
341 }),
342 configs,
343 options: this.options,
344 resolverRunner: new _ResolverRunner.default({
345 config: new _ParcelConfig.default((0, _nullthrows.default)((0, _nullthrows.default)(configs.get('parcel')).result), this.options.packageManager),
346 options: this.options
347 }),
348 pluginOptions: new _PluginOptions.default(this.options),
349 workerApi: this.workerApi
350 };
351 return pipeline;
352 }
353
354 async loadNextPipeline({
355 filePath,
356 isSource,
357 nextType,
358 currentPipeline
359 }) {
360 let nextFilePath = filePath.slice(0, -_path.default.extname(filePath).length) + '.' + nextType;
361 let nextPipeline = await this.loadPipeline(nextFilePath, isSource, this.request.pipeline);
362
363 if (nextPipeline.id === currentPipeline.id) {
364 return null;
365 }
366
367 return nextPipeline;
368 }
369
370 loadTransformerConfig({
371 filePath,
372 plugin,
373 parcelConfigPath,
374 isSource
375 }) {
376 let configRequest = {
377 filePath,
378 env: this.request.env,
379 plugin,
380 isSource,
381 meta: {
382 parcelConfigPath
383 }
384 };
385 return this.loadConfig(configRequest);
386 }
387
388}
389
390exports.default = Transformation;
391
392async function runTransformer(pipeline, asset, transformer, transformerName, preloadedConfig) {
393 const logger = new _logger.PluginLogger({
394 origin: transformerName
395 });
396
397 const resolve = async (from, to) => {
398 return (0, _nullthrows.default)((await pipeline.resolverRunner.resolve((0, _Dependency.createDependency)({
399 env: asset.value.env,
400 moduleSpecifier: to,
401 sourcePath: from
402 })))).filePath;
403 }; // Load config for the transformer.
404
405
406 let config = preloadedConfig;
407
408 if (transformer.getConfig) {
409 // TODO: deprecate getConfig
410 config = await transformer.getConfig({
411 asset: new _Asset.MutableAsset(asset),
412 options: pipeline.pluginOptions,
413 resolve,
414 logger
415 });
416 } // If an ast exists on the asset, but we cannot reuse it,
417 // use the previous transform to generate code that we can re-parse.
418
419
420 if (asset.ast && (!transformer.canReuseAST || !transformer.canReuseAST({
421 ast: asset.ast,
422 options: pipeline.pluginOptions,
423 logger
424 })) && pipeline.generate) {
425 let output = await pipeline.generate(new _Asset.MutableAsset(asset));
426 asset.content = output.code;
427 asset.ast = null;
428 } // Parse if there is no AST available from a previous transform.
429
430
431 if (!asset.ast && transformer.parse) {
432 asset.ast = await transformer.parse({
433 asset: new _Asset.MutableAsset(asset),
434 config,
435 options: pipeline.pluginOptions,
436 resolve,
437 logger
438 });
439 } // Transform.
440
441
442 let results = await normalizeAssets(( // $FlowFixMe
443 await transformer.transform({
444 asset: new _Asset.MutableAsset(asset),
445 config,
446 options: pipeline.pluginOptions,
447 resolve,
448 logger
449 }))); // Create generate and postProcess functions that can be called later
450
451 pipeline.generate = input => {
452 if (transformer.generate) {
453 return Promise.resolve(transformer.generate({
454 asset: input,
455 config,
456 options: pipeline.pluginOptions,
457 resolve,
458 logger
459 }));
460 }
461
462 throw new Error('Asset has an AST but no generate method is available on the transform');
463 }; // For Flow
464
465
466 let postProcess = transformer.postProcess;
467
468 if (postProcess) {
469 pipeline.postProcess = async assets => {
470 let results = await postProcess.call(transformer, {
471 assets: assets.map(asset => new _Asset.MutableAsset(asset)),
472 config,
473 options: pipeline.pluginOptions,
474 resolve,
475 logger
476 });
477 return Promise.all(results.map(result => asset.createChildAsset(result)));
478 };
479 }
480
481 return results;
482}
483
484async function finalize(asset, generate) {
485 if (asset.ast && generate) {
486 let result = await generate(new _Asset.MutableAsset(asset));
487 return asset.createChildAsset(_objectSpread({
488 type: asset.value.type,
489 uniqueKey: asset.value.uniqueKey
490 }, result));
491 }
492
493 return asset;
494}
495
496function normalizeAssets(results) {
497 return results.map(result => {
498 if (!(result instanceof _Asset.MutableAsset)) {
499 return result;
500 }
501
502 let internalAsset = (0, _Asset.assetToInternalAsset)(result);
503 return {
504 type: result.type,
505 content: internalAsset.content,
506 ast: result.ast,
507 map: internalAsset.map,
508 // $FlowFixMe
509 dependencies: [...internalAsset.value.dependencies.values()],
510 includedFiles: result.getIncludedFiles(),
511 // $FlowFixMe
512 env: result.env,
513 isIsolated: result.isIsolated,
514 isInline: result.isInline,
515 pipeline: internalAsset.value.pipeline,
516 meta: result.meta,
517 uniqueKey: internalAsset.value.uniqueKey
518 };
519 });
520}
521
522function getImpactfulConfigInfo(configs) {
523 let impactfulConfigInfo = {};
524
525 for (let [configType, {
526 devDeps,
527 resultHash
528 }] of configs) {
529 let devDepsObject = {};
530
531 for (let [moduleName, version] of devDeps) {
532 devDepsObject[moduleName] = version;
533 }
534
535 impactfulConfigInfo[configType] = {
536 devDeps: devDepsObject,
537 resultHash
538 };
539 }
540
541 return impactfulConfigInfo;
542}
\No newline at end of file