UNPKG

@parcel/core

Version:
539 lines (526 loc) • 19.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _stream() { const data = require("stream"); _stream = function () { return data; }; return data; } function _nullthrows() { const data = _interopRequireDefault(require("nullthrows")); _nullthrows = function () { return data; }; return data; } function _logger() { const data = _interopRequireWildcard(require("@parcel/logger")); _logger = function () { return data; }; return data; } function _diagnostic() { const data = _interopRequireWildcard(require("@parcel/diagnostic")); _diagnostic = function () { return data; }; return data; } function _utils() { const data = require("@parcel/utils"); _utils = function () { return data; }; return data; } function _rust() { const data = require("@parcel/rust"); _rust = function () { return data; }; return data; } var _Dependency = require("./Dependency"); var _ParcelConfig = _interopRequireDefault(require("./ParcelConfig")); var _PathRequest = require("./requests/PathRequest"); var _Asset = require("./public/Asset"); var _UncommittedAsset = _interopRequireDefault(require("./UncommittedAsset")); var _assetUtils = require("./assetUtils"); var _summarizeRequest = _interopRequireDefault(require("./summarizeRequest")); var _PluginOptions = _interopRequireDefault(require("./public/PluginOptions")); var _utils2 = require("./utils"); var _InternalConfig = require("./InternalConfig"); var _ConfigRequest = require("./requests/ConfigRequest"); var _DevDepRequest = require("./requests/DevDepRequest"); var _projectPath = require("./projectPath"); function _assert() { const data = _interopRequireDefault(require("assert")); _assert = function () { return data; }; return data; } function _profiler() { const data = require("@parcel/profiler"); _profiler = function () { return data; }; return data; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // TODO: eventually call path request as sub requests class Transformation { constructor({ request, options, config, workerApi }) { this.configs = new Map(); this.parcelConfig = config; this.options = options; this.request = request; this.workerApi = workerApi; this.invalidations = (0, _utils2.createInvalidations)(); this.devDepRequests = new Map(); this.pluginDevDeps = []; this.resolverRunner = new _PathRequest.ResolverRunner({ config, options, previousDevDeps: request.devDeps }); this.pluginOptions = new _PluginOptions.default((0, _utils2.optionsProxy)(this.options, option => { this.invalidations.invalidateOnOptionChange.add(option); }, devDep => { this.pluginDevDeps.push(devDep); })); } async run() { let asset = await this.loadAsset(); let existing; if (!asset.mapBuffer && _utils().SOURCEMAP_EXTENSIONS.has(asset.value.type)) { // Load existing sourcemaps, this automatically runs the source contents extraction try { existing = await asset.loadExistingSourcemap(); } catch (err) { _logger().default.verbose([{ origin: '@parcel/core', message: (0, _diagnostic().md)`Could not load existing source map for ${(0, _projectPath.fromProjectPathRelative)(asset.value.filePath)}` }, { origin: '@parcel/core', message: (0, _diagnostic().escapeMarkdown)(err.message) }]); } } if (existing == null && // Don't buffer an entire stream into memory since it may not need sourceContent, // e.g. large binary files !(asset.content instanceof _stream().Readable)) { // If no existing sourcemap was found, initialize asset.sourceContent // with the original contents. This will be used when the transformer // calls setMap to ensure the source content is in the sourcemap. asset.sourceContent = await asset.getCode(); } (0, _DevDepRequest.invalidateDevDeps)(this.request.invalidDevDeps, this.options, this.parcelConfig); let pipeline = await this.loadPipeline(this.request.filePath, asset.value.isSource, asset.value.pipeline); let assets, error; try { let results = await this.runPipelines(pipeline, asset); await Promise.all(results.map(asset => asset.commit())); assets = results.map(a => a.value); } catch (e) { error = e; } let configRequests = (0, _ConfigRequest.getConfigRequests)([...this.configs.values(), ...this.resolverRunner.configs.values()]); let devDepRequests = (0, _DevDepRequest.getWorkerDevDepRequests)([...this.devDepRequests.values(), ...this.resolverRunner.devDepRequests.values()]); // $FlowFixMe because of $$raw return { $$raw: true, assets, configRequests, // When throwing an error, this (de)serialization is done automatically by the WorkerFarm error: error ? (0, _diagnostic().anyToDiagnostic)(error) : undefined, invalidations: this.invalidations, devDepRequests }; } async loadAsset() { let { filePath, env, code, pipeline, isSource: isSourceOverride, sideEffects, query } = this.request; let { content, size, isSource: summarizedIsSource } = await (0, _summarizeRequest.default)(this.options.inputFS, { filePath: (0, _projectPath.fromProjectPath)(this.options.projectRoot, filePath), code }); // Prefer `isSource` originating from the AssetRequest. // If the transformer request passed code, use a hash in addition // to the filename as the base for the id to ensure it is unique. let idBase = (0, _projectPath.fromProjectPathRelative)(filePath); if (code != null) { idBase += (0, _rust().hashString)(code); } return new _UncommittedAsset.default({ idBase, value: (0, _assetUtils.createAsset)(this.options.projectRoot, { idBase, filePath, isSource: isSourceOverride ?? summarizedIsSource, type: _path().default.extname((0, _projectPath.fromProjectPathRelative)(filePath)).slice(1), pipeline, env, query, stats: { time: 0, size }, sideEffects }), options: this.options, content, invalidations: this.invalidations }); } async runPipelines(pipeline, initialAsset) { let initialType = initialAsset.value.type; let assets; try { assets = await this.runPipeline(pipeline, initialAsset); } finally { // Add dev dep requests for each transformer for (let transformer of pipeline.transformers) { await this.addDevDependency({ specifier: transformer.name, resolveFrom: transformer.resolveFrom, range: transformer.range }); } // Add dev dep requests for dependencies of transformer plugins // (via proxied packageManager.require calls). for (let devDep of this.pluginDevDeps) { await this.addDevDependency(devDep); } } let finalAssets = []; for (let asset of assets) { let nextPipeline; if (asset.value.type !== initialType) { nextPipeline = await this.loadNextPipeline({ filePath: initialAsset.value.filePath, isSource: asset.value.isSource, newType: asset.value.type, newPipeline: asset.value.pipeline, currentPipeline: pipeline }); } if (nextPipeline) { let nextPipelineAssets = await this.runPipelines(nextPipeline, asset); finalAssets = finalAssets.concat(nextPipelineAssets); } else { finalAssets.push(asset); } } if (!pipeline.postProcess) { return finalAssets; } (0, _assert().default)(pipeline.postProcess != null); let processedFinalAssets = (await pipeline.postProcess(finalAssets)) ?? []; return processedFinalAssets; } async addDevDependency(opts) { let { specifier, resolveFrom, range } = opts; let key = `${specifier}:${(0, _projectPath.fromProjectPathRelative)(resolveFrom)}`; if (this.devDepRequests.has(key)) { return; } // Ensure that the package manager has an entry for this resolution. try { await this.options.packageManager.resolve(specifier, (0, _projectPath.fromProjectPath)(this.options.projectRoot, opts.resolveFrom), { range }); } catch (err) { // ignore } let devDepRequest = await (0, _DevDepRequest.createDevDependency)(opts, this.request.devDeps, this.options); this.devDepRequests.set(key, devDepRequest); } async runPipeline(pipeline, initialAsset) { if (pipeline.transformers.length === 0) { return [initialAsset]; } let initialType = initialAsset.value.type; let inputAssets = [initialAsset]; let resultingAssets = []; let finalAssets = []; for (let transformer of pipeline.transformers) { resultingAssets = []; for (let asset of inputAssets) { if (asset.value.type !== initialType && (await this.loadNextPipeline({ filePath: initialAsset.value.filePath, isSource: asset.value.isSource, newType: asset.value.type, newPipeline: asset.value.pipeline, currentPipeline: pipeline }))) { finalAssets.push(asset); continue; } try { const measurement = _profiler().tracer.createMeasurement(transformer.name, 'transform', (0, _projectPath.fromProjectPathRelative)(initialAsset.value.filePath)); let transformerResults = await this.runTransformer(pipeline, asset, transformer.plugin, transformer.name, transformer.config, transformer.configKeyPath, this.parcelConfig); measurement && measurement.end(); for (let result of transformerResults) { if (result instanceof _UncommittedAsset.default) { resultingAssets.push(result); continue; } resultingAssets.push(asset.createChildAsset(result, transformer.name, this.parcelConfig.filePath, transformer.configKeyPath)); } } catch (e) { let diagnostic = (0, _diagnostic().errorToDiagnostic)(e, { origin: transformer.name, filePath: (0, _projectPath.fromProjectPath)(this.options.projectRoot, asset.value.filePath) }); // If this request is a virtual asset that might not exist on the filesystem, // add the `code` property to each code frame in the diagnostics that match the // request's filepath. This can't be done by the transformer because it might not // have access to the original code (e.g. an inline script tag in HTML). if (this.request.code != null) { for (let d of diagnostic) { if (d.codeFrames) { for (let codeFrame of d.codeFrames) { if (codeFrame.code == null && codeFrame.filePath === this.request.filePath) { codeFrame.code = this.request.code; } } } } } throw new (_diagnostic().default)({ diagnostic }); } } inputAssets = resultingAssets; } // Make assets with ASTs generate unless they are CSS modules. This parallelizes generation // and distributes work more evenly across workers than if one worker needed to // generate all assets in a large bundle during packaging. await Promise.all(resultingAssets.filter(asset => asset.ast != null && !(this.options.mode === 'production' && asset.value.type === 'css' && asset.value.symbols)).map(async asset => { if (asset.isASTDirty && asset.generate) { var _output$map; let output = await asset.generate(); asset.content = output.content; asset.mapBuffer = (_output$map = output.map) === null || _output$map === void 0 ? void 0 : _output$map.toBuffer(); } asset.clearAST(); })); return finalAssets.concat(resultingAssets); } async loadPipeline(filePath, isSource, pipeline) { let transformers = await this.parcelConfig.getTransformers(filePath, pipeline, this.request.isURL); for (let transformer of transformers) { let config = await this.loadTransformerConfig(transformer, isSource); if (config) { this.configs.set(transformer.name, config); } } return { id: transformers.map(t => t.name).join(':'), transformers: transformers.map(transformer => { var _this$configs$get; return { name: transformer.name, resolveFrom: transformer.resolveFrom, config: (_this$configs$get = this.configs.get(transformer.name)) === null || _this$configs$get === void 0 ? void 0 : _this$configs$get.result, configKeyPath: transformer.keyPath, plugin: transformer.plugin }; }), options: this.options, pluginOptions: this.pluginOptions, workerApi: this.workerApi }; } async loadNextPipeline({ filePath, isSource, newType, newPipeline, currentPipeline }) { let filePathRelative = (0, _projectPath.fromProjectPathRelative)(filePath); let nextFilePath = (0, _projectPath.toProjectPathUnsafe)(filePathRelative.slice(0, -_path().default.extname(filePathRelative).length) + '.' + newType); let nextPipeline = await this.loadPipeline(nextFilePath, isSource, newPipeline); if (nextPipeline.id === currentPipeline.id) { return null; } return nextPipeline; } async loadTransformerConfig(transformer, isSource) { let loadConfig = transformer.plugin.loadConfig; if (!loadConfig) { return; } let config = (0, _InternalConfig.createConfig)({ plugin: transformer.name, isSource, searchPath: this.request.filePath, env: this.request.env }); await (0, _ConfigRequest.loadPluginConfig)(transformer, config, this.options); for (let devDep of config.devDeps) { await this.addDevDependency(devDep); } return config; } async runTransformer(pipeline, asset, transformer, transformerName, preloadedConfig, configKeyPath, parcelConfig) { var _transformer$parse; if (asset.transformers.has(transformerName)) { return [asset]; } asset.transformers.add(transformerName); const logger = new (_logger().PluginLogger)({ origin: transformerName }); const tracer = new (_profiler().PluginTracer)({ origin: transformerName, category: 'transform' }); const resolve = async (from, to, options) => { let result = await this.resolverRunner.resolve((0, _Dependency.createDependency)(this.options.projectRoot, { env: asset.value.env, specifier: to, specifierType: (options === null || options === void 0 ? void 0 : options.specifierType) || 'esm', packageConditions: options === null || options === void 0 ? void 0 : options.packageConditions, sourcePath: from })); if (result.invalidateOnFileCreate) { this.invalidations.invalidateOnFileCreate.push(...result.invalidateOnFileCreate.map(i => (0, _utils2.invalidateOnFileCreateToInternal)(this.options.projectRoot, i))); } if (result.invalidateOnFileChange) { for (let filePath of result.invalidateOnFileChange) { this.invalidations.invalidateOnFileChange.add((0, _projectPath.toProjectPath)(this.options.projectRoot, filePath)); } } if (result.diagnostics && result.diagnostics.length > 0) { throw new (_diagnostic().default)({ diagnostic: result.diagnostics }); } return (0, _projectPath.fromProjectPath)(this.options.projectRoot, (0, _nullthrows().default)(result.assetGroup).filePath); }; // If an ast exists on the asset, but we cannot reuse it, // use the previous transform to generate code that we can re-parse. if (asset.ast && asset.isASTDirty && (!transformer.canReuseAST || !transformer.canReuseAST({ ast: asset.ast, options: pipeline.pluginOptions, logger, tracer })) && asset.generate) { var _output$map2; let output = await asset.generate(); asset.content = output.content; asset.mapBuffer = (_output$map2 = output.map) === null || _output$map2 === void 0 ? void 0 : _output$map2.toBuffer(); } // Load config for the transformer. let config = preloadedConfig; // Parse if there is no AST available from a previous transform. let parse = (_transformer$parse = transformer.parse) === null || _transformer$parse === void 0 ? void 0 : _transformer$parse.bind(transformer); if (!asset.ast && parse) { let ast = await parse({ asset: new _Asset.Asset(asset), config, options: pipeline.pluginOptions, resolve, logger, tracer }); if (ast) { asset.setAST(ast); asset.isASTDirty = false; } } // Transform. let transfomerResult = // $FlowFixMe the returned IMutableAsset really is a MutableAsset await transformer.transform({ asset: new _Asset.MutableAsset(asset), config, options: pipeline.pluginOptions, resolve, logger, tracer }); let results = await normalizeAssets(this.options, transfomerResult); // Create generate and postProcess function that can be called later asset.generate = () => { let publicAsset = new _Asset.Asset(asset); if (transformer.generate && asset.ast) { let generated = transformer.generate({ asset: publicAsset, ast: asset.ast, options: pipeline.pluginOptions, logger, tracer }); asset.clearAST(); return Promise.resolve(generated); } throw new Error('Asset has an AST but no generate method is available on the transform'); }; let postProcess = transformer.postProcess; if (postProcess) { pipeline.postProcess = async assets => { let results = await postProcess.call(transformer, { assets: assets.map(asset => new _Asset.MutableAsset(asset)), config, options: pipeline.pluginOptions, resolve, logger, tracer }); return Promise.all(results.map(result => asset.createChildAsset(result, transformerName, parcelConfig.filePath // configKeyPath, ))); }; } return results; } } exports.default = Transformation; function normalizeAssets(options, results) { return results.map(result => { if (result instanceof _Asset.MutableAsset) { return (0, _Asset.mutableAssetToUncommittedAsset)(result); } return result; }); }