414 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			414 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| "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;
 | |
| } |