15.2 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.exec = exec;
7exports.findPackageJSONDir = findPackageJSONDir;
8exports.getPostcssImplementation = getPostcssImplementation;
9exports.getPostcssOptions = getPostcssOptions;
10exports.loadConfig = loadConfig;
11exports.normalizeSourceMap = normalizeSourceMap;
12exports.normalizeSourceMapAfterPostcss = normalizeSourceMapAfterPostcss;
13exports.reportError = reportError;
14exports.warningFactory = warningFactory;
15var _path = _interopRequireDefault(require("path"));
16var _url = _interopRequireDefault(require("url"));
17var _module = _interopRequireDefault(require("module"));
18var _cosmiconfig = require("cosmiconfig");
19function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20const parentModule = module;
21const stat = (inputFileSystem, filePath) => new Promise((resolve, reject) => {
22 inputFileSystem.stat(filePath, (err, stats) => {
23 if (err) {
24 reject(err);
25 }
26 resolve(stats);
27 });
28});
29function exec(code, loaderContext) {
30 const {
31 resource,
32 context
33 } = loaderContext;
34 const module = new _module.default(resource, parentModule);
35
36 // eslint-disable-next-line no-underscore-dangle
37 module.paths = _module.default._nodeModulePaths(context);
38 module.filename = resource;
39
40 // eslint-disable-next-line no-underscore-dangle
41 module._compile(code, resource);
42 return module.exports;
43}
44let tsLoader;
45async function loadConfig(loaderContext, config, postcssOptions) {
46 const searchPath = typeof config === "string" ? _path.default.resolve(config) : _path.default.dirname(loaderContext.resourcePath);
47 let stats;
48 try {
49 stats = await stat(loaderContext.fs, searchPath);
50 } catch (errorIgnore) {
51 throw new Error(`No PostCSS config found in: ${searchPath}`);
52 }
53 const moduleName = "postcss";
54 const searchPlaces = [
55 // Prefer popular format
56 "package.json", `${moduleName}.config.js`, `${moduleName}.config.mjs`, `${moduleName}.config.cjs`, `${moduleName}.config.ts`, `${moduleName}.config.mts`, `${moduleName}.config.cts`, `.${moduleName}rc`, `.${moduleName}rc.json`, `.${moduleName}rc.js`, `.${moduleName}rc.mjs`, `.${moduleName}rc.cjs`, `.${moduleName}rc.ts`, `.${moduleName}rc.mts`, `.${moduleName}rc.cts`, `.${moduleName}rc.yaml`, `.${moduleName}rc.yml`, `.config/${moduleName}rc`, `.config/${moduleName}rc.json`, `.config/${moduleName}rc.yaml`, `.config/${moduleName}rc.yml`, `.config/${moduleName}rc.js`, `.config/${moduleName}rc.mjs`, `.config/${moduleName}rc.cjs`, `.config/${moduleName}rc.ts`, `.config/${moduleName}rc.mts`, `.config/${moduleName}rc.cts`];
57 const loaders = {
58 ".js": async (...args) => {
59 let result;
60 try {
61 result = _cosmiconfig.defaultLoadersSync[".js"](...args);
62 } catch (error) {
63 let importESM;
64 try {
65 // eslint-disable-next-line no-new-func
66 importESM = new Function("id", "return import(id);");
67 } catch (e) {
68 importESM = null;
69 }
70 if (error.code === "ERR_REQUIRE_ESM" && _url.default.pathToFileURL && importESM) {
71 const urlForConfig = _url.default.pathToFileURL(args[0]);
72 result = await importESM(urlForConfig);
73 } else {
74 throw error;
75 }
76 }
77 if (result.default) {
78 return result.default;
79 }
80 return result;
81 },
82 ".cjs": _cosmiconfig.defaultLoadersSync[".cjs"],
83 ".mjs": async (...args) => {
84 let result;
85 let importESM;
86 try {
87 // eslint-disable-next-line no-new-func
88 importESM = new Function("id", "return import(id);");
89 } catch (e) {
90 importESM = null;
91 }
92 if (_url.default.pathToFileURL && importESM) {
93 const urlForConfig = _url.default.pathToFileURL(args[0]);
94 result = await importESM(urlForConfig);
95 } else {
96 throw new Error("ESM is not supported");
97 }
98 if (result.default) {
99 return result.default;
100 }
101 return result;
102 }
103 };
104 if (!tsLoader) {
105 const opts = {
106 interopDefault: true
107 };
108 // eslint-disable-next-line global-require, import/no-extraneous-dependencies
109 const jiti = require("jiti")(__filename, opts);
110 tsLoader = filepath => jiti(filepath);
111 }
112 loaders[".cts"] = tsLoader;
113 loaders[".mts"] = tsLoader;
114 loaders[".ts"] = tsLoader;
115 const explorer = (0, _cosmiconfig.cosmiconfig)(moduleName, {
116 searchStrategy: "global",
117 searchPlaces,
118 loaders
119 });
120 let result;
121 try {
122 if (stats.isFile()) {
123 result = await explorer.load(searchPath);
124 } else {
125 result = await explorer.search(searchPath);
126 }
127 } catch (error) {
128 throw error;
129 }
130 if (!result) {
131 return {};
132 }
133 loaderContext.addBuildDependency(result.filepath);
134 loaderContext.addDependency(result.filepath);
135 if (result.isEmpty) {
136 return result;
137 }
138 if (typeof result.config === "function") {
139 const api = {
140 mode: loaderContext.mode,
141 file: loaderContext.resourcePath,
142 // For complex use
143 webpackLoaderContext: loaderContext,
144 // Partial compatibility with `postcss-cli`
145 env: loaderContext.mode,
146 options: postcssOptions || {}
147 };
148 return {
149 ...result,
150 config: result.config(api)
151 };
152 }
153 return result;
154}
155function loadPlugin(plugin, options, file) {
156 try {
157 // eslint-disable-next-line global-require, import/no-dynamic-require
158 let loadedPlugin = require(plugin);
159 if (loadedPlugin.default) {
160 loadedPlugin = loadedPlugin.default;
161 }
162 if (!options || Object.keys(options).length === 0) {
163 return loadedPlugin;
164 }
165 return loadedPlugin(options);
166 } catch (error) {
167 throw new Error(`Loading PostCSS "${plugin}" plugin failed: ${error.message}\n\n(@${file})`);
168 }
169}
170function pluginFactory() {
171 const listOfPlugins = new Map();
172 return plugins => {
173 if (typeof plugins === "undefined") {
174 return listOfPlugins;
175 }
176 if (Array.isArray(plugins)) {
177 for (const plugin of plugins) {
178 if (Array.isArray(plugin)) {
179 const [name, options] = plugin;
180 listOfPlugins.set(name, options);
181 } else if (plugin && typeof plugin === "function") {
182 listOfPlugins.set(plugin);
183 } 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) {
184 const [name] = Object.keys(plugin);
185 const options = plugin[name];
186 if (options === false) {
187 listOfPlugins.delete(name);
188 } else {
189 listOfPlugins.set(name, options);
190 }
191 } else if (plugin) {
192 listOfPlugins.set(plugin);
193 }
194 }
195 } else {
196 const objectPlugins = Object.entries(plugins);
197 for (const [name, options] of objectPlugins) {
198 if (options === false) {
199 listOfPlugins.delete(name);
200 } else {
201 listOfPlugins.set(name, options);
202 }
203 }
204 }
205 return listOfPlugins;
206 };
207}
208async function tryRequireThenImport(module) {
209 let exports;
210 try {
211 // eslint-disable-next-line import/no-dynamic-require, global-require
212 exports = require(module);
213 return exports;
214 } catch (requireError) {
215 let importESM;
216 try {
217 // eslint-disable-next-line no-new-func
218 importESM = new Function("id", "return import(id);");
219 } catch (e) {
220 importESM = null;
221 }
222 if (requireError.code === "ERR_REQUIRE_ESM" && importESM) {
223 exports = await importESM(module);
224 return exports.default;
225 }
226 throw requireError;
227 }
228}
229async function getPostcssOptions(loaderContext, loadedConfig = {}, postcssOptions = {}) {
230 const file = loaderContext.resourcePath;
231 let normalizedPostcssOptions = postcssOptions;
232 if (typeof normalizedPostcssOptions === "function") {
233 normalizedPostcssOptions = normalizedPostcssOptions(loaderContext);
234 }
235 let plugins = [];
236 try {
237 const factory = pluginFactory();
238 if (loadedConfig.config && loadedConfig.config.plugins) {
239 factory(loadedConfig.config.plugins);
240 }
241 factory(normalizedPostcssOptions.plugins);
242 plugins = [...factory()].map(item => {
243 const [plugin, options] = item;
244 if (typeof plugin === "string") {
245 return loadPlugin(plugin, options, file);
246 }
247 return plugin;
248 });
249 } catch (error) {
250 loaderContext.emitError(error);
251 }
252 const processOptionsFromConfig = {
253 ...loadedConfig.config
254 } || {};
255 if (processOptionsFromConfig.from) {
256 processOptionsFromConfig.from = _path.default.resolve(_path.default.dirname(loadedConfig.filepath), processOptionsFromConfig.from);
257 }
258 if (processOptionsFromConfig.to) {
259 processOptionsFromConfig.to = _path.default.resolve(_path.default.dirname(loadedConfig.filepath), processOptionsFromConfig.to);
260 }
261 const processOptionsFromOptions = {
262 ...normalizedPostcssOptions
263 };
264 if (processOptionsFromOptions.from) {
265 processOptionsFromOptions.from = _path.default.resolve(loaderContext.rootContext, processOptionsFromOptions.from);
266 }
267 if (processOptionsFromOptions.to) {
268 processOptionsFromOptions.to = _path.default.resolve(loaderContext.rootContext, processOptionsFromOptions.to);
269 }
270
271 // No need `plugins` and `config` for processOptions
272 const {
273 plugins: __plugins,
274 ...optionsFromConfig
275 } = processOptionsFromConfig;
276 const {
277 config: _config,
278 plugins: _plugins,
279 ...optionsFromOptions
280 } = processOptionsFromOptions;
281 const processOptions = {
282 from: file,
283 to: file,
284 map: false,
285 ...optionsFromConfig,
286 ...optionsFromOptions
287 };
288 if (typeof processOptions.parser === "string") {
289 try {
290 processOptions.parser = await tryRequireThenImport(processOptions.parser);
291 } catch (error) {
292 loaderContext.emitError(new Error(`Loading PostCSS "${processOptions.parser}" parser failed: ${error.message}\n\n(@${file})`));
293 }
294 }
295 if (typeof processOptions.stringifier === "string") {
296 try {
297 processOptions.stringifier = await tryRequireThenImport(processOptions.stringifier);
298 } catch (error) {
299 loaderContext.emitError(new Error(`Loading PostCSS "${processOptions.stringifier}" stringifier failed: ${error.message}\n\n(@${file})`));
300 }
301 }
302 if (typeof processOptions.syntax === "string") {
303 try {
304 processOptions.syntax = await tryRequireThenImport(processOptions.syntax);
305 } catch (error) {
306 loaderContext.emitError(new Error(`Loading PostCSS "${processOptions.syntax}" syntax failed: ${error.message}\n\n(@${file})`));
307 }
308 }
309 if (processOptions.map === true) {
310 // https://github.com/postcss/postcss/blob/master/docs/source-maps.md
311 processOptions.map = {
312 inline: true
313 };
314 }
315 return {
316 plugins,
317 processOptions
318 };
319}
320const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i;
321const ABSOLUTE_SCHEME = /^[a-z0-9+\-.]+:/i;
322function getURLType(source) {
323 if (source[0] === "/") {
324 if (source[1] === "/") {
325 return "scheme-relative";
326 }
327 return "path-absolute";
328 }
329 if (IS_NATIVE_WIN32_PATH.test(source)) {
330 return "path-absolute";
331 }
332 return ABSOLUTE_SCHEME.test(source) ? "absolute" : "path-relative";
333}
334function normalizeSourceMap(map, resourceContext) {
335 let newMap = map;
336
337 // Some loader emit source map as string
338 // Strip any JSON XSSI avoidance prefix from the string (as documented in the source maps specification), and then parse the string as JSON.
339 if (typeof newMap === "string") {
340 newMap = JSON.parse(newMap);
341 }
342 delete newMap.file;
343 const {
344 sourceRoot
345 } = newMap;
346 delete newMap.sourceRoot;
347 if (newMap.sources) {
348 newMap.sources = newMap.sources.map(source => {
349 const sourceType = getURLType(source);
350
351 // Do no touch `scheme-relative` and `absolute` URLs
352 if (sourceType === "path-relative" || sourceType === "path-absolute") {
353 const absoluteSource = sourceType === "path-relative" && sourceRoot ? _path.default.resolve(sourceRoot, _path.default.normalize(source)) : _path.default.normalize(source);
354 return _path.default.relative(resourceContext, absoluteSource);
355 }
356 return source;
357 });
358 }
359 return newMap;
360}
361function normalizeSourceMapAfterPostcss(map, resourceContext) {
362 const newMap = map;
363
364 // result.map.file is an optional property that provides the output filename.
365 // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it.
366 // eslint-disable-next-line no-param-reassign
367 delete newMap.file;
368
369 // eslint-disable-next-line no-param-reassign
370 newMap.sourceRoot = "";
371
372 // eslint-disable-next-line no-param-reassign
373 newMap.sources = newMap.sources.map(source => {
374 if (source.indexOf("<") === 0) {
375 return source;
376 }
377 const sourceType = getURLType(source);
378
379 // Do no touch `scheme-relative`, `path-absolute` and `absolute` types
380 if (sourceType === "path-relative") {
381 return _path.default.resolve(resourceContext, source);
382 }
383 return source;
384 });
385 return newMap;
386}
387function findPackageJSONDir(cwd, statSync) {
388 let dir = cwd;
389 for (;;) {
390 try {
391 if (statSync(_path.default.join(dir, "package.json")).isFile()) {
392 break;
393 }
394 } catch (error) {
395 // Nothing
396 }
397 const parent = _path.default.dirname(dir);
398 if (dir === parent) {
399 dir = null;
400 break;
401 }
402 dir = parent;
403 }
404 return dir;
405}
406function getPostcssImplementation(loaderContext, implementation) {
407 let resolvedImplementation = implementation;
408 if (!implementation || typeof implementation === "string") {
409 const postcssImplPkg = implementation || "postcss";
410
411 // eslint-disable-next-line import/no-dynamic-require, global-require
412 resolvedImplementation = require(postcssImplPkg);
413 }
414
415 // eslint-disable-next-line consistent-return
416 return resolvedImplementation;
417}
418function reportError(loaderContext, callback, error) {
419 if (error.file) {
420 loaderContext.addDependency(error.file);
421 }
422 if (error.name === "CssSyntaxError") {
423 callback(syntaxErrorFactory(error));
424 } else {
425 callback(error);
426 }
427}
428function warningFactory(warning) {
429 let message = "";
430 if (typeof warning.line !== "undefined") {
431 message += `(${warning.line}:${warning.column}) `;
432 }
433 if (typeof warning.plugin !== "undefined") {
434 message += `from "${warning.plugin}" plugin: `;
435 }
436 message += warning.text;
437 if (warning.node) {
438 message += `\n\nCode:\n ${warning.node.toString()}\n`;
439 }
440 const obj = new Error(message, {
441 cause: warning
442 });
443 obj.stack = null;
444 return obj;
445}
446function syntaxErrorFactory(error) {
447 let message = "\nSyntaxError\n\n";
448 if (typeof error.line !== "undefined") {
449 message += `(${error.line}:${error.column}) `;
450 }
451 if (typeof error.plugin !== "undefined") {
452 message += `from "${error.plugin}" plugin: `;
453 }
454 message += error.file ? `${error.file} ` : "<css input> ";
455 message += `${error.reason}`;
456 const code = error.showSourceCode();
457 if (code) {
458 message += `\n\n${code}\n`;
459 }
460 const obj = new Error(message, {
461 cause: error
462 });
463 obj.stack = null;
464 return obj;
465}
\No newline at end of file