UNPKG

@parcel/core

Version:
1,194 lines (1,173 loc) • 51.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TargetResolver = void 0; exports.default = createTargetRequest; exports.skipTarget = skipTarget; function _diagnostic() { const data = _interopRequireWildcard(require("@parcel/diagnostic")); _diagnostic = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _utils() { const data = require("@parcel/utils"); _utils = function () { return data; }; return data; } function _logger() { const data = _interopRequireDefault(require("@parcel/logger")); _logger = function () { return data; }; return data; } var _Environment = require("../Environment"); var _ParcelConfigRequest = _interopRequireWildcard(require("./ParcelConfigRequest")); function _browserslist() { const data = _interopRequireDefault(require("browserslist")); _browserslist = function () { return data; }; return data; } function _jsonSourcemap() { const data = require("@mischnic/json-sourcemap"); _jsonSourcemap = function () { return data; }; return data; } function _assert() { const data = _interopRequireDefault(require("assert")); _assert = function () { return data; }; return data; } function _nullthrows() { const data = _interopRequireDefault(require("nullthrows")); _nullthrows = function () { return data; }; return data; } var _TargetDescriptor = require("../TargetDescriptor.schema"); var _utils2 = require("../utils"); var _projectPath = require("../projectPath"); var _RequestTracker = require("../RequestTracker"); var _Environment2 = require("../public/Environment"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } 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; } // $FlowFixMe const DEFAULT_DIST_DIRNAME = 'dist'; const JS_RE = /\.[mc]?js$/; const JS_EXTENSIONS = ['.js', '.mjs', '.cjs']; const COMMON_TARGETS = { main: { match: JS_RE, extensions: JS_EXTENSIONS }, module: { // module field is always ESM. Don't allow .cjs extension here. match: /\.m?js$/, extensions: ['.js', '.mjs'] }, browser: { match: JS_RE, extensions: JS_EXTENSIONS }, types: { match: /\.d\.ts$/, extensions: ['.d.ts'] } }; const DEFAULT_ENGINES = { node: process.versions.node, browsers: ['last 1 Chrome version', 'last 1 Safari version', 'last 1 Firefox version', 'last 1 Edge version'] }; const type = 'target_request'; function createTargetRequest(input) { return { id: `${type}:${(0, _utils().hashObject)(input)}`, type: _RequestTracker.requestTypes.target_request, run, input }; } function skipTarget(targetName, exclusiveTarget, descriptorSource) { // We skip targets if they have a descriptor.source and don't match the current exclusiveTarget // They will be handled by a separate resolvePackageTargets call from their Entry point // but with exclusiveTarget set. return exclusiveTarget == null ? descriptorSource != null : targetName !== exclusiveTarget; } async function run({ input, api, options }) { let targetResolver = new TargetResolver(api, (0, _utils2.optionsProxy)(options, api.invalidateOnOptionChange)); let targets = await targetResolver.resolve((0, _projectPath.fromProjectPath)(options.projectRoot, input.packagePath), input.target); assertTargetsAreNotEntries(targets, input, options); let configResult = (0, _nullthrows().default)(await api.runRequest((0, _ParcelConfigRequest.default)())); let parcelConfig = (0, _ParcelConfigRequest.getCachedParcelConfig)(configResult, options); // Find named pipelines for each target. let pipelineNames = new Set(parcelConfig.getNamedPipelines()); for (let target of targets) { if (pipelineNames.has(target.name)) { target.pipeline = target.name; } } if (options.logLevel === 'verbose') { await debugResolvedTargets(input, targets, targetResolver.targetInfo, options); } return targets; } class TargetResolver { constructor(api, options) { this.api = api; this.fs = options.inputFS; this.options = options; this.targetInfo = new Map(); } async resolve(rootDir, exclusiveTarget) { let optionTargets = this.options.targets; if (exclusiveTarget != null && optionTargets == null) { optionTargets = [exclusiveTarget]; } let packageTargets = await this.resolvePackageTargets(rootDir, exclusiveTarget); let targets; if (optionTargets) { if (Array.isArray(optionTargets)) { if (optionTargets.length === 0) { throw new (_diagnostic().default)({ diagnostic: { message: `Targets option is an empty array`, origin: '@parcel/core' } }); } // Only build the intersection of the exclusive target and option targets. if (exclusiveTarget != null) { optionTargets = optionTargets.filter(target => target === exclusiveTarget); } // If an array of strings is passed, it's a filter on the resolved package // targets. Load them, and find the matching targets. targets = optionTargets.map(target => { // null means skipped. if (!packageTargets.has(target)) { throw new (_diagnostic().default)({ diagnostic: { message: (0, _diagnostic().md)`Could not find target with name "${target}"`, origin: '@parcel/core' } }); } return packageTargets.get(target); }).filter(Boolean); } else { // Otherwise, it's an object map of target descriptors (similar to those // in package.json). Adapt them to native targets. targets = Object.entries(optionTargets).map(([name, _descriptor]) => { let { distDir, ...descriptor } = parseDescriptor(name, _descriptor, null, JSON.stringify({ targets: optionTargets }, null, '\t')); if (distDir == null) { let optionTargetsString = JSON.stringify(optionTargets, null, '\t'); throw new (_diagnostic().default)({ diagnostic: { message: (0, _diagnostic().md)`Missing distDir for target "${name}"`, origin: '@parcel/core', codeFrames: [{ code: optionTargetsString, codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(optionTargetsString || '', [{ key: `/${name}`, type: 'value' }]) }] } }); } let target = { name, distDir: (0, _projectPath.toProjectPath)(this.options.projectRoot, _path().default.resolve(this.fs.cwd(), distDir)), publicUrl: descriptor.publicUrl ?? this.options.defaultTargetOptions.publicUrl, env: (0, _Environment.createEnvironment)({ engines: descriptor.engines, context: descriptor.context, isLibrary: descriptor.isLibrary ?? this.options.defaultTargetOptions.isLibrary, includeNodeModules: descriptor.includeNodeModules, outputFormat: descriptor.outputFormat ?? this.options.defaultTargetOptions.outputFormat, shouldOptimize: this.options.defaultTargetOptions.shouldOptimize && descriptor.optimize !== false, shouldScopeHoist: this.options.defaultTargetOptions.shouldScopeHoist && descriptor.scopeHoist !== false, sourceMap: normalizeSourceMap(this.options, descriptor.sourceMap) }) }; if (descriptor.distEntry != null) { target.distEntry = descriptor.distEntry; } if (descriptor.source != null) { target.source = descriptor.source; } return target; }).filter(target => !skipTarget(target.name, exclusiveTarget, target.source)); } let serve = this.options.serveOptions; if (serve && targets.length > 0 && targets.every(t => _Environment2.BROWSER_ENVS.has(t.env.context))) { // In serve mode, we only support a single browser target. If the user // provided more than one, or the matching target is not a browser, throw. if (targets.length > 1) { throw new (_diagnostic().default)({ diagnostic: { message: `More than one target is not supported in serve mode`, origin: '@parcel/core' } }); } targets[0].distDir = (0, _projectPath.toProjectPath)(this.options.projectRoot, serve.distDir); } } else { targets = Array.from(packageTargets.values()).filter(Boolean).filter(descriptor => { return descriptor && !skipTarget(descriptor.name, exclusiveTarget, descriptor.source); }); // Explicit targets were not provided. Either use a modern target for server // mode, or simply use the package.json targets. if (this.options.serveOptions && targets.every(t => _Environment2.BROWSER_ENVS.has(t.env.context))) { // In serve mode, we only support a single browser target. Since the user // hasn't specified a target, use one targeting modern browsers for development let distDir = (0, _projectPath.toProjectPath)(this.options.projectRoot, this.options.serveOptions.distDir); let mainTarget = targets.length === 1 ? targets[0] : null; if (mainTarget !== null && mainTarget !== void 0 && mainTarget.env.isLibrary) { let loc = mainTarget.loc; throw new (_diagnostic().default)({ diagnostic: { origin: '@parcel/core', message: (0, _diagnostic().md)` Library targets are not supported in serve mode. `, codeFrames: loc ? [{ filePath: (0, _projectPath.fromProjectPath)(this.options.projectRoot, loc.filePath), codeHighlights: [(0, _diagnostic().convertSourceLocationToHighlight)(loc, 'Target declared here')] }] : [], hints: [`The "${mainTarget.name}" field is meant for libraries, not applications. Either remove the "${mainTarget.name}" field or choose a different target name.`], documentationURL: 'https://parceljs.org/features/targets/#library-targets' } }); } let context = (mainTarget === null || mainTarget === void 0 ? void 0 : mainTarget.env.context) ?? 'browser'; let engines = _Environment2.BROWSER_ENVS.has(context) ? { browsers: DEFAULT_ENGINES.browsers } : { node: DEFAULT_ENGINES.node }; targets = [{ name: 'default', distDir, publicUrl: this.options.defaultTargetOptions.publicUrl ?? '/', env: (0, _Environment.createEnvironment)({ context, engines, includeNodeModules: mainTarget === null || mainTarget === void 0 ? void 0 : mainTarget.env.includeNodeModules, shouldOptimize: this.options.defaultTargetOptions.shouldOptimize, outputFormat: (mainTarget === null || mainTarget === void 0 ? void 0 : mainTarget.env.outputFormat) ?? this.options.defaultTargetOptions.outputFormat, shouldScopeHoist: this.options.defaultTargetOptions.shouldScopeHoist, sourceMap: this.options.defaultTargetOptions.sourceMaps ? {} : undefined }) }]; } } return targets; } async resolvePackageTargets(rootDir, exclusiveTarget) { let rootFile = _path().default.join(rootDir, 'index'); let conf = await (0, _utils().loadConfig)(this.fs, rootFile, ['package.json'], this.options.projectRoot); let rootFileProject = (0, _projectPath.toProjectPath)(this.options.projectRoot, rootFile); // Invalidate whenever a package.json file is added. this.api.invalidateOnFileCreate({ fileName: 'package.json', aboveFilePath: rootFileProject }); let pkg; let pkgContents; let pkgFilePath; let pkgDir; let pkgMap; if (conf) { pkg = conf.config; let pkgFile = conf.files[0]; if (pkgFile == null) { throw new (_diagnostic().default)({ diagnostic: { message: (0, _diagnostic().md)`Expected package.json file in ${rootDir}`, origin: '@parcel/core' } }); } let _pkgFilePath = pkgFilePath = pkgFile.filePath; // For Flow pkgDir = _path().default.dirname(_pkgFilePath); pkgContents = await this.fs.readFile(_pkgFilePath, 'utf8'); pkgMap = (0, _jsonSourcemap().parse)(pkgContents, undefined, { tabWidth: 1 }); let pp = (0, _projectPath.toProjectPath)(this.options.projectRoot, _pkgFilePath); this.api.invalidateOnFileUpdate(pp); this.api.invalidateOnFileDelete(pp); } else { pkg = {}; pkgDir = this.fs.cwd(); } let pkgTargets = pkg.targets || {}; let pkgEngines = parseEngines(pkg.engines, pkgFilePath, pkgContents, '/engines', 'Invalid engines in package.json') || {}; let browsersLoc = { path: '/engines/browsers' }; let nodeLoc = { path: '/engines/node' }; if (pkgEngines.browsers == null) { let env = this.options.env.BROWSERSLIST_ENV ?? this.options.env.NODE_ENV ?? this.options.mode; if (pkg.browserslist != null) { let pkgBrowserslist = pkg.browserslist; let browserslist = typeof pkgBrowserslist === 'object' && !Array.isArray(pkgBrowserslist) ? pkgBrowserslist[env] : pkgBrowserslist; pkgEngines = { ...pkgEngines, browsers: browserslist }; browsersLoc = { path: '/browserslist' }; } else { let browserslistConfig = await (0, _utils().resolveConfig)(this.fs, _path().default.join(rootDir, 'index'), ['browserslist', '.browserslistrc'], this.options.projectRoot); this.api.invalidateOnFileCreate({ fileName: 'browserslist', aboveFilePath: rootFileProject }); this.api.invalidateOnFileCreate({ fileName: '.browserslistrc', aboveFilePath: rootFileProject }); if (browserslistConfig != null) { let contents = await this.fs.readFile(browserslistConfig, 'utf8'); let config = _browserslist().default.parseConfig(contents); let browserslistBrowsers = config[env] || config.defaults; let pp = (0, _projectPath.toProjectPath)(this.options.projectRoot, browserslistConfig); if ((browserslistBrowsers === null || browserslistBrowsers === void 0 ? void 0 : browserslistBrowsers.length) > 0) { pkgEngines = { ...pkgEngines, browsers: browserslistBrowsers }; browsersLoc = { message: `(defined in ${_path().default.relative(process.cwd(), browserslistConfig)})` }; } // Invalidate whenever browserslist config file or relevant environment variables change this.api.invalidateOnFileUpdate(pp); this.api.invalidateOnFileDelete(pp); this.api.invalidateOnEnvChange('BROWSERSLIST_ENV'); this.api.invalidateOnEnvChange('NODE_ENV'); } } } let targets = new Map(); let node = pkgEngines.node; let browsers = pkgEngines.browsers; let defaultEngines = this.options.defaultTargetOptions.engines; let context = browsers ?? node == null ? 'browser' : 'node'; if (context === 'browser' && pkgEngines.browsers == null) { pkgEngines = { ...pkgEngines, browsers: (defaultEngines === null || defaultEngines === void 0 ? void 0 : defaultEngines.browsers) ?? DEFAULT_ENGINES.browsers }; browsersLoc = { message: '(default)' }; } else if (context === 'node' && pkgEngines.node == null) { pkgEngines = { ...pkgEngines, node: (defaultEngines === null || defaultEngines === void 0 ? void 0 : defaultEngines.node) ?? DEFAULT_ENGINES.node }; nodeLoc = { message: '(default)' }; } // If there is a separate `browser` target, or an `engines.node` field but no browser targets, then // the `main` and `module` targets refer to node, otherwise browser. let mainContext = pkg.browser ?? pkgTargets.browser ?? (node != null && browsers == null) ? 'node' : 'browser'; let mainContextLoc = pkg.browser != null ? { inferred: '/browser', message: '(because a browser field also exists)', type: 'key' } : pkgTargets.browser ? { inferred: '/targets/browser', message: '(because a browser target also exists)', type: 'key' } : node != null && browsers == null ? nodeLoc.path ? { inferred: nodeLoc.path, message: '(because node engines were defined)', type: 'key' } : nodeLoc : { message: '(default)' }; let moduleContext = pkg.browser ?? pkgTargets.browser ? 'browser' : mainContext; let moduleContextLoc = pkg.browser != null ? { inferred: '/browser', message: '(because a browser field also exists)', type: 'key' } : pkgTargets.browser ? { inferred: '/targets/browser', message: '(becausea browser target also exists)', type: 'key' } : mainContextLoc; let getEnginesLoc = (targetName, descriptor) => { let enginesLoc = `/targets/${targetName}/engines`; switch (context) { case 'browser': case 'web-worker': case 'service-worker': case 'worklet': { if (descriptor.engines) { return { path: enginesLoc + '/browsers' }; } else { return browsersLoc; } } case 'node': { if (descriptor.engines) { return { path: enginesLoc + '/node' }; } else { return nodeLoc; } } case 'electron-main': case 'electron-renderer': { var _descriptor$engines, _pkgEngines; if (((_descriptor$engines = descriptor.engines) === null || _descriptor$engines === void 0 ? void 0 : _descriptor$engines.electron) != null) { return { path: enginesLoc + '/electron' }; } else if (((_pkgEngines = pkgEngines) === null || _pkgEngines === void 0 ? void 0 : _pkgEngines.electron) != null) { return { path: '/engines/electron' }; } } } return { message: '(default)' }; }; for (let targetName in COMMON_TARGETS) { let _targetDist; let pointer; if (targetName === 'browser' && pkg[targetName] != null && typeof pkg[targetName] === 'object' && pkg.name) { // The `browser` field can be a file path or an alias map. _targetDist = pkg[targetName][pkg.name]; pointer = `/${targetName}/${(0, _diagnostic().encodeJSONKeyComponent)(pkg.name)}`; } else { _targetDist = pkg[targetName]; pointer = `/${targetName}`; } // For Flow let targetDist = _targetDist; if (typeof targetDist === 'string' || pkgTargets[targetName]) { let distDir; let distEntry; let loc; (0, _assert().default)(pkgMap != null); let _descriptor = pkgTargets[targetName] ?? {}; if (typeof targetDist === 'string') { distDir = (0, _projectPath.toProjectPath)(this.options.projectRoot, _path().default.resolve(pkgDir, _path().default.dirname(targetDist))); distEntry = _path().default.basename(targetDist); loc = { filePath: (0, _nullthrows().default)(pkgFilePath), ...(0, _diagnostic().getJSONSourceLocation)(pkgMap.pointers[pointer], 'value') }; } else { distDir = this.options.defaultTargetOptions.distDir ?? (0, _projectPath.toProjectPath)(this.options.projectRoot, _path().default.join(pkgDir, DEFAULT_DIST_DIRNAME, targetName)); } if (_descriptor == false) { continue; } let descriptor = parseCommonTargetDescriptor(targetName, _descriptor, pkgFilePath, pkgContents); if (skipTarget(targetName, exclusiveTarget, descriptor.source)) { targets.set(targetName, null); continue; } if (distEntry != null && !COMMON_TARGETS[targetName].match.test(distEntry)) { let contents = typeof pkgContents === 'string' ? pkgContents : // $FlowFixMe JSON.stringify(pkgContents, null, '\t'); // $FlowFixMe let listFormat = new Intl.ListFormat('en-US', { type: 'disjunction' }); let extensions = listFormat.format(COMMON_TARGETS[targetName].extensions); let ext = _path().default.extname(distEntry); throw new (_diagnostic().default)({ diagnostic: { message: (0, _diagnostic().md)`Unexpected output file type ${ext} in target "${targetName}"`, origin: '@parcel/core', codeFrames: [{ language: 'json', filePath: pkgFilePath ?? undefined, code: contents, codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{ key: pointer, type: 'value', message: `File extension must be ${extensions}` }]) }], hints: [`The "${targetName}" field is meant for libraries. If you meant to output a ${ext} file, either remove the "${targetName}" field or choose a different target name.`], documentationURL: 'https://parceljs.org/features/targets/#library-targets' } }); } if (descriptor.outputFormat === 'global') { let contents = typeof pkgContents === 'string' ? pkgContents : // $FlowFixMe JSON.stringify(pkgContents, null, '\t'); throw new (_diagnostic().default)({ diagnostic: { message: (0, _diagnostic().md)`The "global" output format is not supported in the "${targetName}" target.`, origin: '@parcel/core', codeFrames: [{ language: 'json', filePath: pkgFilePath ?? undefined, code: contents, codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{ key: `/targets/${targetName}/outputFormat`, type: 'value' }]) }], hints: [`The "${targetName}" field is meant for libraries. The outputFormat must be either "commonjs" or "esmodule". Either change or remove the declared outputFormat.`], documentationURL: 'https://parceljs.org/features/targets/#library-targets' } }); } let [inferredOutputFormat, inferredOutputFormatField] = this.inferOutputFormat(distEntry, descriptor, targetName, pkg, pkgFilePath, pkgContents); let outputFormat = descriptor.outputFormat ?? this.options.defaultTargetOptions.outputFormat ?? inferredOutputFormat ?? (targetName === 'module' ? 'esmodule' : 'commonjs'); let isModule = outputFormat === 'esmodule'; if (targetName === 'main' && outputFormat === 'esmodule' && inferredOutputFormat !== 'esmodule') { let contents = typeof pkgContents === 'string' ? pkgContents : // $FlowFixMe JSON.stringify(pkgContents, null, '\t'); throw new (_diagnostic().default)({ diagnostic: { // prettier-ignore message: (0, _diagnostic().md)`Output format "esmodule" cannot be used in the "main" target without a .mjs extension or "type": "module" field.`, origin: '@parcel/core', codeFrames: [{ language: 'json', filePath: pkgFilePath ?? undefined, code: contents, codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{ key: `/targets/${targetName}/outputFormat`, type: 'value', message: 'Declared output format defined here' }, { key: '/main', type: 'value', message: 'Inferred output format defined here' }]) }], hints: [`Either change the output file extension to .mjs, add "type": "module" to package.json, or remove the declared outputFormat.`], documentationURL: 'https://parceljs.org/features/targets/#library-targets' } }); } if (descriptor.scopeHoist === false) { let contents = typeof pkgContents === 'string' ? pkgContents : // $FlowFixMe JSON.stringify(pkgContents, null, '\t'); throw new (_diagnostic().default)({ diagnostic: { message: 'Scope hoisting cannot be disabled for library targets.', origin: '@parcel/core', codeFrames: [{ language: 'json', filePath: pkgFilePath ?? undefined, code: contents, codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{ key: `/targets/${targetName}/scopeHoist`, type: 'value' }]) }], hints: [`The "${targetName}" target is meant for libraries. Either remove the "scopeHoist" option, or use a different target name.`], documentationURL: 'https://parceljs.org/features/targets/#library-targets' } }); } let context = descriptor.context ?? (targetName === 'browser' ? 'browser' : isModule ? moduleContext : mainContext); let engines = descriptor.engines ?? pkgEngines; if (context === 'browser' && engines.browsers == null) { engines = { ...engines, browsers: (defaultEngines === null || defaultEngines === void 0 ? void 0 : defaultEngines.browsers) ?? DEFAULT_ENGINES.browsers }; } else if (context === 'node' && engines.node == null) { engines = { ...engines, node: (defaultEngines === null || defaultEngines === void 0 ? void 0 : defaultEngines.node) ?? DEFAULT_ENGINES.node }; } targets.set(targetName, { name: targetName, distDir, distEntry, publicUrl: descriptor.publicUrl ?? this.options.defaultTargetOptions.publicUrl, env: (0, _Environment.createEnvironment)({ engines, context, includeNodeModules: descriptor.includeNodeModules ?? false, outputFormat, isLibrary: true, shouldOptimize: this.options.defaultTargetOptions.shouldOptimize && descriptor.optimize === true, shouldScopeHoist: true, sourceMap: normalizeSourceMap(this.options, descriptor.sourceMap) }), loc: (0, _utils2.toInternalSourceLocation)(this.options.projectRoot, loc) }); this.targetInfo.set(targetName, { output: { path: pointer }, engines: getEnginesLoc(targetName, descriptor), context: descriptor.context ? { path: `/targets/${targetName}/context` } : targetName === 'browser' ? { message: '(inferred from target name)', inferred: pointer, type: 'key' } : isModule ? moduleContextLoc : mainContextLoc, includeNodeModules: descriptor.includeNodeModules ? { path: `/targets/${targetName}/includeNodeModules`, type: 'key' } : { message: '(default)' }, outputFormat: descriptor.outputFormat ? { path: `/targets/${targetName}/outputFormat` } : inferredOutputFormatField === '/type' ? { message: `(inferred from package.json#type)`, inferred: inferredOutputFormatField } : inferredOutputFormatField != null ? { message: `(inferred from file extension)`, inferred: inferredOutputFormatField } : { message: '(default)' }, isLibrary: { message: '(default)' }, shouldOptimize: descriptor.optimize ? { path: `/targets/${targetName}/optimize` } : { message: '(default)' }, shouldScopeHoist: { message: '(default)' } }); } } let customTargets = Object.keys(pkgTargets).filter(targetName => !COMMON_TARGETS[targetName]); // Custom targets for (let targetName of customTargets) { let distPath = pkg[targetName]; let distDir; let distEntry; let loc; let pointer; if (distPath == null) { distDir = (0, _projectPath.fromProjectPath)(this.options.projectRoot, this.options.defaultTargetOptions.distDir) ?? _path().default.join(pkgDir, DEFAULT_DIST_DIRNAME); if (customTargets.length >= 2) { distDir = _path().default.join(distDir, targetName); } (0, _assert().default)(pkgMap != null); (0, _assert().default)(typeof pkgFilePath === 'string'); loc = { filePath: pkgFilePath, ...(0, _diagnostic().getJSONSourceLocation)(pkgMap.pointers[`/targets/${targetName}`], 'key') }; } else { if (typeof distPath !== 'string') { let contents = typeof pkgContents === 'string' ? pkgContents : // $FlowFixMe JSON.stringify(pkgContents, null, '\t'); throw new (_diagnostic().default)({ diagnostic: { message: (0, _diagnostic().md)`Invalid distPath for target "${targetName}"`, origin: '@parcel/core', codeFrames: [{ language: 'json', filePath: pkgFilePath ?? undefined, code: contents, codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{ key: `/${targetName}`, type: 'value', message: 'Expected type string' }]) }] } }); } distDir = _path().default.resolve(pkgDir, _path().default.dirname(distPath)); distEntry = _path().default.basename(distPath); (0, _assert().default)(typeof pkgFilePath === 'string'); (0, _assert().default)(pkgMap != null); loc = { filePath: pkgFilePath, ...(0, _diagnostic().getJSONSourceLocation)(pkgMap.pointers[`/${targetName}`], 'value') }; pointer = `/${targetName}`; } if (targetName in pkgTargets) { let descriptor = parsePackageDescriptor(targetName, pkgTargets[targetName], pkgFilePath, pkgContents); let pkgDir = _path().default.dirname((0, _nullthrows().default)(pkgFilePath)); if (skipTarget(targetName, exclusiveTarget, descriptor.source)) { targets.set(targetName, null); continue; } let [inferredOutputFormat, inferredOutputFormatField] = this.inferOutputFormat(distEntry, descriptor, targetName, pkg, pkgFilePath, pkgContents); if (descriptor.scopeHoist === false && descriptor.isLibrary) { let contents = typeof pkgContents === 'string' ? pkgContents : // $FlowFixMe JSON.stringify(pkgContents, null, '\t'); throw new (_diagnostic().default)({ diagnostic: { message: 'Scope hoisting cannot be disabled for library targets.', origin: '@parcel/core', codeFrames: [{ language: 'json', filePath: pkgFilePath ?? undefined, code: contents, codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{ key: `/targets/${targetName}/scopeHoist`, type: 'value' }, { key: `/targets/${targetName}/isLibrary`, type: 'value' }]) }], hints: [`Either remove the "scopeHoist" or "isLibrary" option.`], documentationURL: 'https://parceljs.org/features/targets/#library-targets' } }); } let isLibrary = descriptor.isLibrary ?? this.options.defaultTargetOptions.isLibrary ?? false; let shouldScopeHoist = isLibrary ? true : this.options.defaultTargetOptions.shouldScopeHoist; let engines = descriptor.engines ?? pkgEngines; if (descriptor.context === 'browser' && engines.browsers == null) { engines = { ...engines, browsers: (defaultEngines === null || defaultEngines === void 0 ? void 0 : defaultEngines.browsers) ?? DEFAULT_ENGINES.browsers }; } else if (descriptor.context === 'node' && engines.node == null) { engines = { ...engines, node: (defaultEngines === null || defaultEngines === void 0 ? void 0 : defaultEngines.node) ?? DEFAULT_ENGINES.node }; } targets.set(targetName, { name: targetName, distDir: (0, _projectPath.toProjectPath)(this.options.projectRoot, descriptor.distDir != null ? _path().default.resolve(pkgDir, descriptor.distDir) : distDir), distEntry, publicUrl: descriptor.publicUrl ?? this.options.defaultTargetOptions.publicUrl, env: (0, _Environment.createEnvironment)({ engines, context: descriptor.context, includeNodeModules: descriptor.includeNodeModules, outputFormat: descriptor.outputFormat ?? this.options.defaultTargetOptions.outputFormat ?? inferredOutputFormat ?? undefined, isLibrary, shouldOptimize: this.options.defaultTargetOptions.shouldOptimize && ( // Libraries are not optimized by default, users must explicitly configure this. isLibrary ? descriptor.optimize === true : descriptor.optimize !== false), shouldScopeHoist: shouldScopeHoist && descriptor.scopeHoist !== false, sourceMap: normalizeSourceMap(this.options, descriptor.sourceMap) }), loc: (0, _utils2.toInternalSourceLocation)(this.options.projectRoot, loc) }); this.targetInfo.set(targetName, { output: pointer != null ? { path: pointer } : { message: '(default)' }, engines: getEnginesLoc(targetName, descriptor), context: descriptor.context ? { path: `/targets/${targetName}/context` } : { message: '(default)' }, includeNodeModules: descriptor.includeNodeModules ? { path: `/targets/${targetName}/includeNodeModules`, type: 'key' } : { message: '(default)' }, outputFormat: descriptor.outputFormat ? { path: `/targets/${targetName}/outputFormat` } : inferredOutputFormatField === '/type' ? { message: `(inferred from package.json#type)`, inferred: inferredOutputFormatField } : inferredOutputFormatField != null ? { message: `(inferred from file extension)`, inferred: inferredOutputFormatField } : { message: '(default)' }, isLibrary: descriptor.isLibrary != null ? { path: `/targets/${targetName}/isLibrary` } : { message: '(default)' }, shouldOptimize: descriptor.optimize != null ? { path: `/targets/${targetName}/optimize` } : { message: '(default)' }, shouldScopeHoist: descriptor.scopeHoist != null ? { path: `/targets/${targetName}/scopeHoist` } : { message: '(default)' } }); } } // If no explicit targets were defined, add a default. if (targets.size === 0) { targets.set('default', { name: 'default', distDir: this.options.defaultTargetOptions.distDir ?? (0, _projectPath.toProjectPath)(this.options.projectRoot, _path().default.join(pkgDir, DEFAULT_DIST_DIRNAME)), publicUrl: this.options.defaultTargetOptions.publicUrl, env: (0, _Environment.createEnvironment)({ engines: pkgEngines, context, outputFormat: this.options.defaultTargetOptions.outputFormat, isLibrary: this.options.defaultTargetOptions.isLibrary, shouldOptimize: this.options.defaultTargetOptions.shouldOptimize, shouldScopeHoist: this.options.defaultTargetOptions.shouldScopeHoist ?? (this.options.mode === 'production' && !this.options.defaultTargetOptions.isLibrary), sourceMap: this.options.defaultTargetOptions.sourceMaps ? {} : undefined }) }); } assertNoDuplicateTargets(this.options, targets, pkgFilePath, pkgContents); return targets; } inferOutputFormat(distEntry, descriptor, targetName, pkg, pkgFilePath, pkgContents) { // Infer the outputFormat based on package.json properties. // If the extension is .mjs it's always a module. // If the extension is .cjs, it's always commonjs. // If the "type" field is set to "module" and the extension is .js, it's a module. let ext = distEntry != null ? _path().default.extname(distEntry) : null; let inferredOutputFormat, inferredOutputFormatField; switch (ext) { case '.mjs': inferredOutputFormat = 'esmodule'; inferredOutputFormatField = `/${targetName}`; break; case '.cjs': inferredOutputFormat = 'commonjs'; inferredOutputFormatField = `/${targetName}`; break; case '.js': if (pkg.type === 'module') { inferredOutputFormat = 'esmodule'; inferredOutputFormatField = '/type'; } break; } if (descriptor.outputFormat && inferredOutputFormat && descriptor.outputFormat !== inferredOutputFormat) { let contents = typeof pkgContents === 'string' ? pkgContents : // $FlowFixMe JSON.stringify(pkgContents, null, '\t'); let expectedExtensions; switch (descriptor.outputFormat) { case 'esmodule': expectedExtensions = ['.mjs', '.js']; break; case 'commonjs': expectedExtensions = ['.cjs', '.js']; break; case 'global': expectedExtensions = ['.js']; break; } // $FlowFixMe let listFormat = new Intl.ListFormat('en-US', { type: 'disjunction' }); throw new (_diagnostic().default)({ diagnostic: { message: (0, _diagnostic().md)`Declared output format "${descriptor.outputFormat}" does not match expected output format "${inferredOutputFormat}".`, origin: '@parcel/core', codeFrames: [{ language: 'json', filePath: pkgFilePath ?? undefined, code: contents, codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{ key: `/targets/${targetName}/outputFormat`, type: 'value', message: 'Declared output format defined here' }, { key: (0, _nullthrows().default)(inferredOutputFormatField), type: 'value', message: 'Inferred output format defined here' }]) }], hints: [inferredOutputFormatField === '/type' ? 'Either remove the target\'s declared "outputFormat" or remove the "type" field.' : `Either remove the target's declared "outputFormat" or change the extension to ${listFormat.format(expectedExtensions)}.`], documentationURL: 'https://parceljs.org/features/targets/#library-targets' } }); } return [inferredOutputFormat, inferredOutputFormatField]; } } exports.TargetResolver = TargetResolver; function parseEngines(engines, pkgPath, pkgContents, prependKey, message) { if (engines === undefined) { return engines; } else { _utils().validateSchema.diagnostic(_TargetDescriptor.ENGINES_SCHEMA, { data: engines, source: pkgContents, filePath: pkgPath, prependKey }, '@parcel/core', message); // $FlowFixMe we just verified this return engines; } } function parseDescriptor(targetName, descriptor, pkgPath, pkgContents) { _utils().validateSchema.diagnostic(_TargetDescriptor.DESCRIPTOR_SCHEMA, { data: descriptor, source: pkgContents, filePath: pkgPath, prependKey: `/targets/${targetName}` }, '@parcel/core', `Invalid target descriptor for target "${targetName}"`); // $FlowFixMe we just verified this return descriptor; } function parsePackageDescriptor(targetName, descriptor, pkgPath, pkgContents) { _utils().validateSchema.diagnostic(_TargetDescriptor.PACKAGE_DESCRIPTOR_SCHEMA, { data: descriptor, source: pkgContents, filePath: pkgPath, prependKey: `/targets/${targetName}` }, '@parcel/core', `Invalid target descriptor for target "${targetName}"`); // $FlowFixMe we just verified this return descriptor; } function parseCommonTargetDescriptor(targetName, descriptor, pkgPath, pkgContents) { _utils().validateSchema.diagnostic(_TargetDescriptor.COMMON_TARGET_DESCRIPTOR_SCHEMA, { data: descriptor, source: pkgContents, filePath: pkgPath, prependKey: `/targets/${targetName}` }, '@parcel/core', `Invalid target descriptor for target "${targetName}"`); // $FlowFixMe we just verified this return descriptor; } function assertNoDuplicateTargets(options, targets, pkgFilePath, pkgContents) { // Detect duplicate targets by destination path and provide a nice error. // Without this, an assertion is thrown much later after naming the bundles and finding duplicates. let targetsByPath = new Map(); for (let target of targets.values()) { if (!target) { continue; } let { distEntry } = target; if (distEntry != null) { var _targetsByPath$get; let distPath = _path().default.join((0, _projectPath.fromProjectPath)(options.projectRoot, target.distDir), distEntry); if (!targetsByPath.has(distPath)) { targetsByPath.set(distPath, []); } (_targetsByPath$get = targetsByPath.get(distPath)) === null || _targetsByPath$get === void 0 || _targetsByPath$get.push(target.name); } } let diagnostics = []; for (let [targetPath, targetNames] of targetsByPath) { if (targetNames.length > 1 && pkgContents != null && pkgFilePath != null) { diagnostics.push({ message: (0, _diagnostic().md)`Multiple targets have the same destination path "${_path().default.relative(_path().default.dirname(pkgFilePath), targetPath)}"`, origin: '@parcel/core', codeFrames: [{ language: 'json', filePath: pkgFilePath || undefined, code: pkgContents, codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(pkgContents, targetNames.map(t => ({ key: `/${t}`, type: 'value' }))) }] }); } } if (diagnostics.length > 0) { // Only add hints to the last diagnostic so it isn't duplicated on each one diagnostics[diagnostics.length - 1].hints = ['Try removing the duplicate targets, or changing the destination paths.']; throw new (_diagnostic().default)({ diagnostic: diagnostics }); } } function normalizeSourceMap(options, sourceMap) { if (options.defaultTargetOptions.sourceMaps) { if (typeof sourceMap === 'boolean') { return sourceMap ? {} : undefined; } else { return sourceMap ?? {}; } } else { return undefined; } } function assertTargetsAreNotEntries(targets, input, options) { for (const target of targets) { if (target.distEntry != null && (0, _projectPath.joinProjectPath)(target.distDir, target.distEntry) === input.filePath) { let loc = target.loc; let relativeEntry = _path().default.relative(process.cwd(), (0, _projectPath.fromProjectPath)(options.projectRoot, input.filePath)); let codeFrames = []; if (loc) { codeFrames.push({ filePath: (0, _projectPath.fromProjectPath)(options.projectRoot, loc.filePath), codeHighlights: [(0, _diagnostic().convertSourceLocationToHighlight)(loc, 'Target defined here')] }); let inputLoc = input.loc; if (inputLoc) { let highlight = (0, _diagnostic().convertSourceLocationToHighlight)(inputLoc, 'Entry defined here'); if (inputLoc.filePath === loc.filePath) { codeFrames[0].codeHighlights.push(highlight); } else { codeFrames.push({ filePath: (0, _projectPath.fromProjectPath)(options.projectRoot, inputLoc.filePath), codeHighlights: [highlight] }); } } } throw new (_diagnostic().default)({ diagnostic: { origin: '@parcel/core', message: `Target "${target.name}" is configured to overwrite entry "${relativeEntry}".`, codeFrames, hints: [(COMMON_TARGETS[target.name] ? `The "${target.name}" field is an _output_ file path so that your build can be consumed by other tools. ` : '') + `Change the "${target.name}" field to point to an output file rather than your source code.`], documentationURL: 'https://parceljs.org/features/targets/' } }); } } } async function debugResolvedTargets(input, targets, targetInfo, options) { for (let target of targets) { let info = targetInfo.get(target.name); let loc = target.loc; if (!loc || !info) { continue; } let output = (0, _projectPath.fromProjectPath)(options.projectRoot, target.distDir); if (target.distEntry != null) { output = _path().default.join(output, target.distEntry); } // Resolve relevant engines for context. let engines; switch (target.env.context) { case 'browser': case 'web-worker': case 'service-worker': case 'worklet': { let browsers = target.env.engines.browsers; engines = Array.isArray(browsers) ? browsers.join(', ') : browsers; break; } case 'node': engines = target.env.engines.node; break; case 'electron-main': case 'electron-renderer': engines = target.env.engines.electron; break; } let highlights = []; if (input.loc) { highlights.push((0, _diagnostic().convertSourceLocationToHighlight)(input.loc, 'entry defined here')); } // Read package.json where target is defined. let targetFilePath = (0, _projectPath.fromProjectPath)(options.projectRoot, loc.filePath); let contents = await options.inputFS.readFile(targetFilePath, 'utf8'); // Builds up map of code highlights for each defined/inferred path in the package.json. let jsonHighlights = new Map(); for (let key in info) { let keyInfo = info[key]; let path = keyInfo.path || keyInfo.inferred; if (!path) { continue; } let type = keyInfo.type || 'value'; let highlight = jsonHighlights.get(path); if (!highlight) { highlight = { type: type, defined: '', inferred: [] }; jsonHighlights.set(path, highlight); } else if (highlight.type !== type) { highlight.type = null; } if (keyInfo.path) { highlight.defined = (0, _diagnostic().md)`${key} defined here`; } if (keyInfo.inferred) { highlight.inferred.push((0, _diagnostic().md)`${key} to be ${JSON.stringify(target.env[key])}`); } } // $FlowFixMe let listFormat = new Intl.ListFormat('en-US'); // Generate human friendly messages for each field. let highlightsWithMessages = [...jsonHighlights].map(([k, v]) => { let message = v.defined; if (v.inferred.length > 0) { message += (message ? ', ' : '') + 'caused '; message += listFormat.format(v.inferred); } return { key: k, type: v.type, message }; }); // Get code highlights from JSON paths. highlights.push(...(0, _diagnostic().generateJSONCodeHighlights)(contents, highlightsWithMessages)); // Format includeNodeModules to be human readable. let includeNodeModules; if (typeof target.env.includeNodeModules === 'boolean') { includeNodeModules = String(target.env.includeNodeModules); } else if (Array.isArray(target.env.includeNodeModules)) { includeNodeModules = 'only ' + listFormat.format(target.env.includeNodeM