UNPKG

3.59 kBJavaScriptView Raw
1const localRequire = require('../utils/localRequire');
2const loadPlugins = require('../utils/loadPlugins');
3const md5 = require('../utils/md5');
4const postcss = require('postcss');
5const FileSystemLoader = require('css-modules-loader-core/lib/file-system-loader');
6const semver = require('semver');
7const path = require('path');
8const fs = require('@parcel/fs');
9
10module.exports = async function(asset) {
11 let config = await getConfig(asset);
12 if (!config) {
13 return;
14 }
15
16 await asset.parseIfNeeded();
17 let res = await postcss(config.plugins).process(asset.getCSSAst(), config);
18
19 asset.ast.css = res.css;
20 asset.ast.dirty = false;
21 asset.sourceMap = res.map ? res.map.toJSON() : null;
22};
23
24async function getConfig(asset) {
25 let config = await asset.getConfig(
26 ['.postcssrc', '.postcssrc.json', '.postcssrc.js', 'postcss.config.js'],
27 {packageKey: 'postcss'}
28 );
29
30 let enableModules =
31 asset.options.rendition && asset.options.rendition.modules;
32 if (!config && !asset.options.minify && !enableModules) {
33 return;
34 }
35
36 config = config || {};
37
38 if (asset.options.sourceMaps) {
39 config.map = {inline: false, annotation: false, sourcesContent: true};
40 }
41
42 if (typeof config !== 'object') {
43 throw new Error('PostCSS config should be an object.');
44 }
45
46 let postcssModulesConfig = {
47 getJSON: (filename, json) => (asset.cssModules = json),
48 Loader: createLoader(asset),
49 generateScopedName: (name, filename) =>
50 `_${name}_${md5(filename).substr(0, 5)}`
51 };
52
53 if (config.plugins && config.plugins['postcss-modules']) {
54 postcssModulesConfig = Object.assign(
55 config.plugins['postcss-modules'],
56 postcssModulesConfig
57 );
58 delete config.plugins['postcss-modules'];
59 }
60
61 config.plugins = await loadPlugins(config.plugins, asset.name);
62
63 if (config.modules || enableModules) {
64 let postcssModules = await localRequire('postcss-modules', asset.name);
65 config.plugins.push(postcssModules(postcssModulesConfig));
66 }
67
68 if (asset.options.minify) {
69 let cssnano = await localRequire('cssnano', asset.name);
70 let {version} = await localRequire('cssnano/package.json', asset.name);
71 config.plugins.push(
72 cssnano(
73 (await asset.getConfig(['cssnano.config.js'])) || {
74 // Only enable safe css transforms if cssnano < 4
75 // See: https://github.com/parcel-bundler/parcel/issues/698
76 // See: https://github.com/ben-eb/cssnano/releases/tag/v4.0.0-rc.0
77 safe: semver.satisfies(version, '<4.0.0-rc')
78 }
79 )
80 );
81 }
82
83 config.from = asset.name;
84 config.to = asset.name;
85 return config;
86}
87
88const createLoader = asset =>
89 class ParcelFileSystemLoader extends FileSystemLoader {
90 async fetch(composesPath, relativeTo) {
91 let importPath = composesPath.replace(/^["']|["']$/g, '');
92 const {resolved} = asset.resolveDependency(importPath, relativeTo);
93 let rootRelativePath = path.resolve(path.dirname(relativeTo), resolved);
94 const root = path.resolve('/');
95 // fixes an issue on windows which is part of the css-modules-loader-core
96 // see https://github.com/css-modules/css-modules-loader-core/issues/230
97 if (rootRelativePath.startsWith(root)) {
98 rootRelativePath = rootRelativePath.substr(root.length);
99 }
100
101 const source = await fs.readFile(resolved, 'utf-8');
102 const {exportTokens} = await this.core.load(
103 source,
104 rootRelativePath,
105 undefined,
106 this.fetch.bind(this)
107 );
108 return exportTokens;
109 }
110
111 get finalSource() {
112 return '';
113 }
114 };