@parcel/core
Version:
316 lines (315 loc) • 11.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function _diagnostic() {
const data = _interopRequireWildcard(require("@parcel/diagnostic"));
_diagnostic = function () {
return data;
};
return data;
}
function _json() {
const data = _interopRequireDefault(require("json5"));
_json = function () {
return data;
};
return data;
}
function _nullthrows() {
const data = _interopRequireDefault(require("nullthrows"));
_nullthrows = function () {
return data;
};
return data;
}
function _utils() {
const data = require("@parcel/utils");
_utils = function () {
return data;
};
return data;
}
function _path() {
const data = require("path");
_path = function () {
return data;
};
return data;
}
var _loadParcelPlugin = _interopRequireDefault(require("./loadParcelPlugin"));
var _projectPath = require("./projectPath");
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; }
class ParcelConfig {
constructor(config, options) {
this.options = options;
this.filePath = config.filePath;
this.resolvers = config.resolvers || [];
this.transformers = config.transformers || {};
this.runtimes = config.runtimes || [];
this.bundler = config.bundler;
this.namers = config.namers || [];
this.packagers = config.packagers || {};
this.optimizers = config.optimizers || {};
this.compressors = config.compressors || {};
this.reporters = config.reporters || [];
this.validators = config.validators || {};
this.pluginCache = new Map();
this.regexCache = new Map();
}
static deserialize(serialized) {
return new ParcelConfig(serialized.config, serialized.options);
}
getConfig() {
return {
filePath: this.filePath,
resolvers: this.resolvers,
transformers: this.transformers,
validators: this.validators,
runtimes: this.runtimes,
bundler: this.bundler,
namers: this.namers,
packagers: this.packagers,
optimizers: this.optimizers,
compressors: this.compressors,
reporters: this.reporters
};
}
serialize() {
return {
$$raw: false,
config: this.getConfig(),
options: this.options
};
}
_loadPlugin(node) {
let plugin = this.pluginCache.get(node.packageName);
if (plugin) {
return plugin;
}
plugin = (0, _loadParcelPlugin.default)(node.packageName, (0, _projectPath.fromProjectPath)(this.options.projectRoot, node.resolveFrom), node.keyPath, this.options);
this.pluginCache.set(node.packageName, plugin);
return plugin;
}
async loadPlugin(node) {
let plugin = await this._loadPlugin(node);
if (!plugin) {
return null;
}
return {
...plugin,
name: node.packageName,
keyPath: node.keyPath
};
}
invalidatePlugin(packageName) {
this.pluginCache.delete(packageName);
}
async loadPlugins(plugins) {
return (await Promise.all(plugins.map(p => this.loadPlugin(p)))).filter(Boolean);
}
async getResolvers() {
if (this.resolvers.length === 0) {
throw await this.missingPluginError(this.resolvers, 'No resolver plugins specified in .parcelrc config', '/resolvers');
}
return this.loadPlugins(this.resolvers);
}
_getValidatorNodes(filePath) {
let validators = this.matchGlobMapPipelines(filePath, this.validators) || [];
return validators;
}
getValidatorNames(filePath) {
let validators = this._getValidatorNodes(filePath);
return validators.map(v => v.packageName);
}
getValidators(filePath) {
let validators = this._getValidatorNodes(filePath);
return this.loadPlugins(validators);
}
getNamedPipelines() {
return Object.keys(this.transformers).filter(glob => glob.includes(':')).map(glob => glob.split(':')[0]);
}
async getTransformers(filePath, pipeline, allowEmpty) {
let transformers = this.matchGlobMapPipelines(filePath, this.transformers, pipeline);
if (!transformers || transformers.length === 0) {
if (allowEmpty) {
return [];
}
throw await this.missingPluginError(this.transformers, (0, _diagnostic().md)`No transformers found for __${(0, _projectPath.fromProjectPathRelative)(filePath)}__` + (pipeline != null ? ` with pipeline: '${pipeline}'` : '') + '.', '/transformers');
}
return this.loadPlugins(transformers);
}
async getBundler() {
if (!this.bundler) {
throw await this.missingPluginError([], 'No bundler specified in .parcelrc config', '/bundler');
}
return (0, _nullthrows().default)(await this.loadPlugin(this.bundler));
}
async getNamers() {
if (this.namers.length === 0) {
throw await this.missingPluginError(this.namers, 'No namer plugins specified in .parcelrc config', '/namers');
}
return this.loadPlugins(this.namers);
}
getRuntimes() {
if (!this.runtimes) {
return Promise.resolve([]);
}
return this.loadPlugins(this.runtimes);
}
async getPackager(filePath, pipeline) {
// If a pipeline is specified, but it doesn't exist in the optimizers config, ignore it.
// Pipelines for bundles come from their entry assets, so the pipeline likely exists in transformers.
if (pipeline) {
let prefix = pipeline + ':';
if (!Object.keys(this.packagers).some(glob => glob.startsWith(prefix))) {
pipeline = null;
}
}
let packager = this.matchGlobMap((0, _projectPath.toProjectPathUnsafe)(filePath), this.packagers, pipeline);
if (!packager) {
throw await this.missingPluginError(this.packagers, (0, _diagnostic().md)`No packager found for __${filePath}__.`, '/packagers');
}
return (0, _nullthrows().default)(await this.loadPlugin(packager));
}
_getOptimizerNodes(filePath, pipeline) {
// If a pipeline is specified, but it doesn't exist in the optimizers config, ignore it.
// Pipelines for bundles come from their entry assets, so the pipeline likely exists in transformers.
if (pipeline) {
let prefix = pipeline + ':';
if (!Object.keys(this.optimizers).some(glob => glob.startsWith(prefix))) {
pipeline = null;
}
}
return this.matchGlobMapPipelines((0, _projectPath.toProjectPathUnsafe)(filePath), this.optimizers, pipeline) ?? [];
}
getOptimizerNames(filePath, pipeline) {
let optimizers = this._getOptimizerNodes(filePath, pipeline);
return optimizers.map(o => o.packageName);
}
getOptimizers(filePath, pipeline) {
let optimizers = this._getOptimizerNodes(filePath, pipeline);
if (optimizers.length === 0) {
return Promise.resolve([]);
}
return this.loadPlugins(optimizers);
}
async getCompressors(filePath) {
let compressors = this.matchGlobMapPipelines((0, _projectPath.toProjectPathUnsafe)(filePath), this.compressors) ?? [];
if (compressors.length === 0) {
throw await this.missingPluginError(this.compressors, (0, _diagnostic().md)`No compressors found for __${filePath}__.`, '/compressors');
}
return this.loadPlugins(compressors);
}
getReporters() {
return this.loadPlugins(this.reporters);
}
isGlobMatch(projectPath, pattern, pipeline) {
// glob's shouldn't be dependant on absolute paths anyway
let filePath = (0, _projectPath.fromProjectPathRelative)(projectPath);
let [patternPipeline, patternGlob] = pattern.split(':');
if (!patternGlob) {
patternGlob = patternPipeline;
patternPipeline = null;
}
let re = this.regexCache.get(patternGlob);
if (!re) {
re = (0, _utils().globToRegex)(patternGlob, {
dot: true,
nocase: true
});
this.regexCache.set(patternGlob, re);
}
return (pipeline === patternPipeline || !pipeline && !patternPipeline) && (re.test(filePath) || re.test((0, _path().basename)(filePath)));
}
matchGlobMap(filePath, globMap, pipeline) {
for (let pattern in globMap) {
if (this.isGlobMatch(filePath, pattern, pipeline)) {
return globMap[pattern];
}
}
return null;
}
matchGlobMapPipelines(filePath, globMap, pipeline) {
let matches = [];
if (pipeline) {
// If a pipeline is requested, a the glob needs to match exactly
let exactMatch;
for (let pattern in globMap) {
if (this.isGlobMatch(filePath, pattern, pipeline)) {
exactMatch = globMap[pattern];
break;
}
}
if (!exactMatch) {
return [];
} else {
matches.push(exactMatch);
}
}
for (let pattern in globMap) {
if (this.isGlobMatch(filePath, pattern)) {
matches.push(globMap[pattern]);
}
}
let flatten = () => {
let pipeline = matches.shift() || [];
let spreadIndex = pipeline.indexOf('...');
if (spreadIndex >= 0) {
pipeline = [...pipeline.slice(0, spreadIndex), ...flatten(), ...pipeline.slice(spreadIndex + 1)];
}
if (pipeline.includes('...')) {
throw new Error('Only one spread parameter can be included in a config pipeline');
}
return pipeline;
};
let res = flatten();
// $FlowFixMe afaik this should work
return res;
}
async missingPluginError(plugins, message, key) {
let configsWithPlugin;
if (Array.isArray(plugins)) {
configsWithPlugin = new Set(getConfigPaths(this.options, plugins));
} else {
configsWithPlugin = new Set(Object.keys(plugins).flatMap(k => Array.isArray(plugins[k]) ? getConfigPaths(this.options, plugins[k]) : [getConfigPath(this.options, plugins[k])]));
}
if (configsWithPlugin.size === 0) {
configsWithPlugin.add((0, _projectPath.fromProjectPath)(this.options.projectRoot, this.filePath));
}
let seenKey = false;
let codeFrames = await Promise.all([...configsWithPlugin].map(async filePath => {
let configContents = await this.options.inputFS.readFile(filePath, 'utf8');
if (!_json().default.parse(configContents)[key.slice(1)]) {
key = '';
} else {
seenKey = true;
}
return {
filePath,
code: configContents,
codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(configContents, [{
key
}])
};
}));
return new (_diagnostic().default)({
diagnostic: {
message,
origin: '@parcel/core',
codeFrames,
hints: !seenKey ? ['Try extending __@parcel/config-default__'] : []
}
});
}
}
exports.default = ParcelConfig;
function getConfigPaths(options, nodes) {
return nodes.map(node => node !== '...' ? getConfigPath(options, node) : null).filter(Boolean);
}
function getConfigPath(options, node) {
return (0, _projectPath.fromProjectPath)(options.projectRoot, node.resolveFrom);
}