You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
414 lines
11 KiB
414 lines
11 KiB
"use strict"; |
|
|
|
Object.defineProperty(exports, "__esModule", { |
|
value: true |
|
}); |
|
exports.exec = exec; |
|
exports.findPackageJSONDir = findPackageJSONDir; |
|
exports.getPostcssImplementation = getPostcssImplementation; |
|
exports.getPostcssOptions = getPostcssOptions; |
|
exports.loadConfig = loadConfig; |
|
exports.normalizeSourceMap = normalizeSourceMap; |
|
exports.normalizeSourceMapAfterPostcss = normalizeSourceMapAfterPostcss; |
|
|
|
var _path = _interopRequireDefault(require("path")); |
|
|
|
var _module = _interopRequireDefault(require("module")); |
|
|
|
var _full = require("klona/full"); |
|
|
|
var _cosmiconfig = require("cosmiconfig"); |
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } |
|
|
|
const parentModule = module; |
|
|
|
const stat = (inputFileSystem, filePath) => new Promise((resolve, reject) => { |
|
inputFileSystem.stat(filePath, (err, stats) => { |
|
if (err) { |
|
reject(err); |
|
} |
|
|
|
resolve(stats); |
|
}); |
|
}); |
|
|
|
function exec(code, loaderContext) { |
|
const { |
|
resource, |
|
context |
|
} = loaderContext; |
|
const module = new _module.default(resource, parentModule); // eslint-disable-next-line no-underscore-dangle |
|
|
|
module.paths = _module.default._nodeModulePaths(context); |
|
module.filename = resource; // eslint-disable-next-line no-underscore-dangle |
|
|
|
module._compile(code, resource); |
|
|
|
return module.exports; |
|
} |
|
|
|
async function loadConfig(loaderContext, config, postcssOptions) { |
|
const searchPath = typeof config === "string" ? _path.default.resolve(config) : _path.default.dirname(loaderContext.resourcePath); |
|
let stats; |
|
|
|
try { |
|
stats = await stat(loaderContext.fs, searchPath); |
|
} catch (errorIgnore) { |
|
throw new Error(`No PostCSS config found in: ${searchPath}`); |
|
} |
|
|
|
const explorer = (0, _cosmiconfig.cosmiconfig)("postcss"); |
|
let result; |
|
|
|
try { |
|
if (stats.isFile()) { |
|
result = await explorer.load(searchPath); |
|
} else { |
|
result = await explorer.search(searchPath); |
|
} |
|
} catch (error) { |
|
throw error; |
|
} |
|
|
|
if (!result) { |
|
return {}; |
|
} |
|
|
|
loaderContext.addBuildDependency(result.filepath); |
|
loaderContext.addDependency(result.filepath); |
|
|
|
if (result.isEmpty) { |
|
return result; |
|
} |
|
|
|
if (typeof result.config === "function") { |
|
const api = { |
|
mode: loaderContext.mode, |
|
file: loaderContext.resourcePath, |
|
// For complex use |
|
webpackLoaderContext: loaderContext, |
|
// Partial compatibility with `postcss-cli` |
|
env: loaderContext.mode, |
|
options: postcssOptions || {} |
|
}; |
|
result.config = result.config(api); |
|
} |
|
|
|
result = (0, _full.klona)(result); |
|
return result; |
|
} |
|
|
|
function loadPlugin(plugin, options, file) { |
|
try { |
|
if (!options || Object.keys(options).length === 0) { |
|
// eslint-disable-next-line global-require, import/no-dynamic-require |
|
const loadedPlugin = require(plugin); |
|
|
|
if (loadedPlugin.default) { |
|
return loadedPlugin.default; |
|
} |
|
|
|
return loadedPlugin; |
|
} // eslint-disable-next-line global-require, import/no-dynamic-require |
|
|
|
|
|
const loadedPlugin = require(plugin); |
|
|
|
if (loadedPlugin.default) { |
|
return loadedPlugin.default(options); |
|
} |
|
|
|
return loadedPlugin(options); |
|
} catch (error) { |
|
throw new Error(`Loading PostCSS "${plugin}" plugin failed: ${error.message}\n\n(@${file})`); |
|
} |
|
} |
|
|
|
function pluginFactory() { |
|
const listOfPlugins = new Map(); |
|
return plugins => { |
|
if (typeof plugins === "undefined") { |
|
return listOfPlugins; |
|
} |
|
|
|
if (Array.isArray(plugins)) { |
|
for (const plugin of plugins) { |
|
if (Array.isArray(plugin)) { |
|
const [name, options] = plugin; |
|
listOfPlugins.set(name, options); |
|
} else if (plugin && typeof plugin === "function") { |
|
listOfPlugins.set(plugin); |
|
} else if (plugin && Object.keys(plugin).length === 1 && (typeof plugin[Object.keys(plugin)[0]] === "object" || typeof plugin[Object.keys(plugin)[0]] === "boolean") && plugin[Object.keys(plugin)[0]] !== null) { |
|
const [name] = Object.keys(plugin); |
|
const options = plugin[name]; |
|
|
|
if (options === false) { |
|
listOfPlugins.delete(name); |
|
} else { |
|
listOfPlugins.set(name, options); |
|
} |
|
} else if (plugin) { |
|
listOfPlugins.set(plugin); |
|
} |
|
} |
|
} else { |
|
const objectPlugins = Object.entries(plugins); |
|
|
|
for (const [name, options] of objectPlugins) { |
|
if (options === false) { |
|
listOfPlugins.delete(name); |
|
} else { |
|
listOfPlugins.set(name, options); |
|
} |
|
} |
|
} |
|
|
|
return listOfPlugins; |
|
}; |
|
} |
|
|
|
async function tryRequireThenImport(module) { |
|
let exports; |
|
|
|
try { |
|
// eslint-disable-next-line import/no-dynamic-require, global-require |
|
exports = require(module); |
|
return exports; |
|
} catch (requireError) { |
|
let importESM; |
|
|
|
try { |
|
// eslint-disable-next-line no-new-func |
|
importESM = new Function("id", "return import(id);"); |
|
} catch (e) { |
|
importESM = null; |
|
} |
|
|
|
if (requireError.code === "ERR_REQUIRE_ESM" && importESM) { |
|
exports = await importESM(module); |
|
return exports.default; |
|
} |
|
|
|
throw requireError; |
|
} |
|
} |
|
|
|
async function getPostcssOptions(loaderContext, loadedConfig = {}, postcssOptions = {}) { |
|
const file = loaderContext.resourcePath; |
|
let normalizedPostcssOptions = postcssOptions; |
|
|
|
if (typeof normalizedPostcssOptions === "function") { |
|
normalizedPostcssOptions = normalizedPostcssOptions(loaderContext); |
|
} |
|
|
|
let plugins = []; |
|
|
|
try { |
|
const factory = pluginFactory(); |
|
|
|
if (loadedConfig.config && loadedConfig.config.plugins) { |
|
factory(loadedConfig.config.plugins); |
|
} |
|
|
|
factory(normalizedPostcssOptions.plugins); |
|
plugins = [...factory()].map(item => { |
|
const [plugin, options] = item; |
|
|
|
if (typeof plugin === "string") { |
|
return loadPlugin(plugin, options, file); |
|
} |
|
|
|
return plugin; |
|
}); |
|
} catch (error) { |
|
loaderContext.emitError(error); |
|
} |
|
|
|
const processOptionsFromConfig = loadedConfig.config || {}; |
|
|
|
if (processOptionsFromConfig.from) { |
|
processOptionsFromConfig.from = _path.default.resolve(_path.default.dirname(loadedConfig.filepath), processOptionsFromConfig.from); |
|
} |
|
|
|
if (processOptionsFromConfig.to) { |
|
processOptionsFromConfig.to = _path.default.resolve(_path.default.dirname(loadedConfig.filepath), processOptionsFromConfig.to); |
|
} // No need them for processOptions |
|
|
|
|
|
delete processOptionsFromConfig.plugins; |
|
const processOptionsFromOptions = (0, _full.klona)(normalizedPostcssOptions); |
|
|
|
if (processOptionsFromOptions.from) { |
|
processOptionsFromOptions.from = _path.default.resolve(loaderContext.rootContext, processOptionsFromOptions.from); |
|
} |
|
|
|
if (processOptionsFromOptions.to) { |
|
processOptionsFromOptions.to = _path.default.resolve(loaderContext.rootContext, processOptionsFromOptions.to); |
|
} // No need them for processOptions |
|
|
|
|
|
delete processOptionsFromOptions.config; |
|
delete processOptionsFromOptions.plugins; |
|
const processOptions = { |
|
from: file, |
|
to: file, |
|
map: false, |
|
...processOptionsFromConfig, |
|
...processOptionsFromOptions |
|
}; |
|
|
|
if (typeof processOptions.parser === "string") { |
|
try { |
|
processOptions.parser = await tryRequireThenImport(processOptions.parser); |
|
} catch (error) { |
|
loaderContext.emitError(new Error(`Loading PostCSS "${processOptions.parser}" parser failed: ${error.message}\n\n(@${file})`)); |
|
} |
|
} |
|
|
|
if (typeof processOptions.stringifier === "string") { |
|
try { |
|
processOptions.stringifier = await tryRequireThenImport(processOptions.stringifier); |
|
} catch (error) { |
|
loaderContext.emitError(new Error(`Loading PostCSS "${processOptions.stringifier}" stringifier failed: ${error.message}\n\n(@${file})`)); |
|
} |
|
} |
|
|
|
if (typeof processOptions.syntax === "string") { |
|
try { |
|
processOptions.syntax = await tryRequireThenImport(processOptions.syntax); |
|
} catch (error) { |
|
loaderContext.emitError(new Error(`Loading PostCSS "${processOptions.syntax}" syntax failed: ${error.message}\n\n(@${file})`)); |
|
} |
|
} |
|
|
|
if (processOptions.map === true) { |
|
// https://github.com/postcss/postcss/blob/master/docs/source-maps.md |
|
processOptions.map = { |
|
inline: true |
|
}; |
|
} |
|
|
|
return { |
|
plugins, |
|
processOptions |
|
}; |
|
} |
|
|
|
const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i; |
|
const ABSOLUTE_SCHEME = /^[a-z0-9+\-.]+:/i; |
|
|
|
function getURLType(source) { |
|
if (source[0] === "/") { |
|
if (source[1] === "/") { |
|
return "scheme-relative"; |
|
} |
|
|
|
return "path-absolute"; |
|
} |
|
|
|
if (IS_NATIVE_WIN32_PATH.test(source)) { |
|
return "path-absolute"; |
|
} |
|
|
|
return ABSOLUTE_SCHEME.test(source) ? "absolute" : "path-relative"; |
|
} |
|
|
|
function normalizeSourceMap(map, resourceContext) { |
|
let newMap = map; // Some loader emit source map as string |
|
// Strip any JSON XSSI avoidance prefix from the string (as documented in the source maps specification), and then parse the string as JSON. |
|
|
|
if (typeof newMap === "string") { |
|
newMap = JSON.parse(newMap); |
|
} |
|
|
|
delete newMap.file; |
|
const { |
|
sourceRoot |
|
} = newMap; |
|
delete newMap.sourceRoot; |
|
|
|
if (newMap.sources) { |
|
newMap.sources = newMap.sources.map(source => { |
|
const sourceType = getURLType(source); // Do no touch `scheme-relative` and `absolute` URLs |
|
|
|
if (sourceType === "path-relative" || sourceType === "path-absolute") { |
|
const absoluteSource = sourceType === "path-relative" && sourceRoot ? _path.default.resolve(sourceRoot, _path.default.normalize(source)) : _path.default.normalize(source); |
|
return _path.default.relative(resourceContext, absoluteSource); |
|
} |
|
|
|
return source; |
|
}); |
|
} |
|
|
|
return newMap; |
|
} |
|
|
|
function normalizeSourceMapAfterPostcss(map, resourceContext) { |
|
const newMap = map; // result.map.file is an optional property that provides the output filename. |
|
// Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it. |
|
// eslint-disable-next-line no-param-reassign |
|
|
|
delete newMap.file; // eslint-disable-next-line no-param-reassign |
|
|
|
newMap.sourceRoot = ""; // eslint-disable-next-line no-param-reassign |
|
|
|
newMap.sources = newMap.sources.map(source => { |
|
if (source.indexOf("<") === 0) { |
|
return source; |
|
} |
|
|
|
const sourceType = getURLType(source); // Do no touch `scheme-relative`, `path-absolute` and `absolute` types |
|
|
|
if (sourceType === "path-relative") { |
|
return _path.default.resolve(resourceContext, source); |
|
} |
|
|
|
return source; |
|
}); |
|
return newMap; |
|
} |
|
|
|
function findPackageJSONDir(cwd, statSync) { |
|
let dir = cwd; |
|
|
|
for (;;) { |
|
try { |
|
if (statSync(_path.default.join(dir, "package.json")).isFile()) { |
|
break; |
|
} |
|
} catch (error) {// Nothing |
|
} |
|
|
|
const parent = _path.default.dirname(dir); |
|
|
|
if (dir === parent) { |
|
dir = null; |
|
break; |
|
} |
|
|
|
dir = parent; |
|
} |
|
|
|
return dir; |
|
} |
|
|
|
function getPostcssImplementation(loaderContext, implementation) { |
|
let resolvedImplementation = implementation; |
|
|
|
if (!implementation || typeof implementation === "string") { |
|
const postcssImplPkg = implementation || "postcss"; |
|
|
|
try { |
|
// eslint-disable-next-line import/no-dynamic-require, global-require |
|
resolvedImplementation = require(postcssImplPkg); |
|
} catch (error) { |
|
loaderContext.emitError(error); // eslint-disable-next-line consistent-return |
|
|
|
return; |
|
} |
|
} // eslint-disable-next-line consistent-return |
|
|
|
|
|
return resolvedImplementation; |
|
} |