@parcel/transformer-postcss 2.0.0-nightly.97 → 2.0.0-nightly.982
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/PostCSSTransformer.js +224 -120
- package/lib/constants.js +8 -0
- package/lib/loadConfig.js +146 -0
- package/lib/loadPlugins.js +6 -4
- package/package.json +18 -9
- package/src/PostCSSTransformer.js +151 -140
- package/src/constants.js +3 -0
- package/src/loadConfig.js +147 -0
- package/src/loadPlugins.js +20 -3
|
@@ -5,205 +5,291 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
function _hash() {
|
|
9
|
+
const data = require("@parcel/hash");
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
_hash = function () {
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
function _utils() {
|
|
19
|
+
const data = require("@parcel/utils");
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
_utils = function () {
|
|
22
|
+
return data;
|
|
23
|
+
};
|
|
17
24
|
|
|
18
|
-
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
function _plugin() {
|
|
29
|
+
const data = require("@parcel/plugin");
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
_plugin = function () {
|
|
32
|
+
return data;
|
|
33
|
+
};
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
25
37
|
|
|
26
|
-
function
|
|
38
|
+
function _fileSystemLoader() {
|
|
39
|
+
const data = _interopRequireDefault(require("css-modules-loader-core/lib/file-system-loader"));
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
_fileSystemLoader = function () {
|
|
42
|
+
return data;
|
|
43
|
+
};
|
|
29
44
|
|
|
30
|
-
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
31
47
|
|
|
32
|
-
function
|
|
48
|
+
function _nullthrows() {
|
|
49
|
+
const data = _interopRequireDefault(require("nullthrows"));
|
|
33
50
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
51
|
+
_nullthrows = function () {
|
|
52
|
+
return data;
|
|
53
|
+
};
|
|
37
54
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
asset,
|
|
41
|
-
resolve,
|
|
42
|
-
options
|
|
43
|
-
}) {
|
|
44
|
-
let configFile = await asset.getConfig(['.postcssrc', '.postcssrc.json', '.postcssrc.js', 'postcss.config.js'], {
|
|
45
|
-
packageKey: 'postcss'
|
|
46
|
-
}); // Use a basic, modules-only PostCSS config if the file opts in by a name
|
|
47
|
-
// like foo.module.css
|
|
48
|
-
|
|
49
|
-
if (configFile == null && asset.filePath.match(MODULE_BY_NAME_RE)) {
|
|
50
|
-
configFile = {
|
|
51
|
-
plugins: {
|
|
52
|
-
'postcss-modules': {}
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
}
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
58
|
+
function _path() {
|
|
59
|
+
const data = _interopRequireDefault(require("path"));
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
_path = function () {
|
|
62
|
+
return data;
|
|
63
|
+
};
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
65
|
+
return data;
|
|
66
|
+
}
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
function _semver() {
|
|
69
|
+
const data = _interopRequireDefault(require("semver"));
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
_semver = function () {
|
|
72
|
+
return data;
|
|
73
|
+
};
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
return data;
|
|
76
|
+
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
function _postcssValueParser() {
|
|
79
|
+
const data = _interopRequireDefault(require("postcss-value-parser"));
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
getJSON: (filename, json) => asset.meta.cssModules = json,
|
|
84
|
-
Loader: createLoader(asset, resolve),
|
|
85
|
-
generateScopedName: (name, filename, css) => `_${name}_${(0, _utils.md5FromString)(filename + css).substr(0, 5)}`
|
|
86
|
-
}, originalModulesConfig)));
|
|
87
|
-
}
|
|
81
|
+
_postcssValueParser = function () {
|
|
82
|
+
return data;
|
|
83
|
+
};
|
|
88
84
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
85
|
+
return data;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function _postcssModules() {
|
|
89
|
+
const data = _interopRequireDefault(require("postcss-modules"));
|
|
90
|
+
|
|
91
|
+
_postcssModules = function () {
|
|
92
|
+
return data;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var _loadConfig = require("./loadConfig");
|
|
99
|
+
|
|
100
|
+
var _constants = require("./constants");
|
|
101
|
+
|
|
102
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
103
|
+
|
|
104
|
+
const COMPOSES_RE = /composes:.+from\s*("|').*("|')\s*;?/;
|
|
105
|
+
const FROM_IMPORT_RE = /.+from\s*(?:"|')(.*)(?:"|')\s*;?/;
|
|
106
|
+
const LEGACY_MODULE_RE = /@value|(:global|:local)(?!\s*\()/i;
|
|
107
|
+
const MODULE_BY_NAME_RE = /\.module\./;
|
|
108
|
+
|
|
109
|
+
var _default = new (_plugin().Transformer)({
|
|
110
|
+
loadConfig({
|
|
111
|
+
config,
|
|
112
|
+
options,
|
|
113
|
+
logger
|
|
114
|
+
}) {
|
|
115
|
+
return (0, _loadConfig.load)({
|
|
116
|
+
config,
|
|
117
|
+
options,
|
|
118
|
+
logger
|
|
119
|
+
});
|
|
94
120
|
},
|
|
95
121
|
|
|
96
122
|
canReuseAST({
|
|
97
123
|
ast
|
|
98
124
|
}) {
|
|
99
|
-
return ast.type === 'postcss' && _semver.default.satisfies(ast.version,
|
|
125
|
+
return ast.type === 'postcss' && _semver().default.satisfies(ast.version, _constants.POSTCSS_RANGE);
|
|
100
126
|
},
|
|
101
127
|
|
|
102
128
|
async parse({
|
|
103
129
|
asset,
|
|
104
|
-
config
|
|
130
|
+
config,
|
|
131
|
+
options
|
|
105
132
|
}) {
|
|
106
|
-
|
|
133
|
+
let isLegacy = await isLegacyCssModule(asset);
|
|
134
|
+
|
|
135
|
+
if (!config && !isLegacy) {
|
|
107
136
|
return;
|
|
108
137
|
}
|
|
109
138
|
|
|
139
|
+
const postcss = await loadPostcss(options, asset.filePath);
|
|
110
140
|
return {
|
|
111
141
|
type: 'postcss',
|
|
112
|
-
version: '
|
|
113
|
-
program:
|
|
142
|
+
version: '8.2.1',
|
|
143
|
+
program: postcss.parse(await asset.getCode(), {
|
|
114
144
|
from: asset.filePath
|
|
115
|
-
})
|
|
145
|
+
}).toJSON()
|
|
116
146
|
};
|
|
117
147
|
},
|
|
118
148
|
|
|
119
149
|
async transform({
|
|
120
150
|
asset,
|
|
121
|
-
config
|
|
151
|
+
config,
|
|
152
|
+
options,
|
|
153
|
+
resolve
|
|
122
154
|
}) {
|
|
155
|
+
asset.type = 'css';
|
|
156
|
+
let isLegacy = await isLegacyCssModule(asset);
|
|
157
|
+
|
|
158
|
+
if (isLegacy && !config) {
|
|
159
|
+
config = {
|
|
160
|
+
hydrated: {
|
|
161
|
+
plugins: [],
|
|
162
|
+
from: asset.filePath,
|
|
163
|
+
to: asset.filePath,
|
|
164
|
+
modules: {}
|
|
165
|
+
}
|
|
166
|
+
}; // TODO: warning?
|
|
167
|
+
}
|
|
168
|
+
|
|
123
169
|
if (!config) {
|
|
124
170
|
return [asset];
|
|
125
171
|
}
|
|
126
172
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
173
|
+
const postcss = await loadPostcss(options, asset.filePath);
|
|
174
|
+
let ast = (0, _nullthrows().default)(await asset.getAST());
|
|
175
|
+
let program = postcss.fromJSON(ast.program);
|
|
176
|
+
let plugins = [...config.hydrated.plugins];
|
|
177
|
+
let cssModules = null;
|
|
178
|
+
|
|
179
|
+
if (config.hydrated.modules) {
|
|
180
|
+
asset.meta.cssModulesCompiled = true;
|
|
181
|
+
plugins.push((0, _postcssModules().default)({
|
|
182
|
+
getJSON: (filename, json) => cssModules = json,
|
|
183
|
+
Loader: createLoader(asset, resolve),
|
|
184
|
+
generateScopedName: (name, filename) => `${name}_${(0, _hash().hashString)(_path().default.relative(options.projectRoot, filename)).substr(0, 6)}`,
|
|
185
|
+
...config.hydrated.modules
|
|
186
|
+
}));
|
|
187
|
+
let code = asset.isASTDirty() ? null : await asset.getCode();
|
|
188
|
+
|
|
189
|
+
if (code == null || COMPOSES_RE.test(code)) {
|
|
190
|
+
program.walkDecls(decl => {
|
|
191
|
+
let [, importPath] = FROM_IMPORT_RE.exec(decl.value) || [];
|
|
192
|
+
|
|
193
|
+
if (decl.prop === 'composes' && importPath != null) {
|
|
194
|
+
let parsed = (0, _postcssValueParser().default)(decl.value);
|
|
195
|
+
parsed.walk(node => {
|
|
196
|
+
if (node.type === 'string') {
|
|
197
|
+
asset.addDependency({
|
|
198
|
+
specifier: importPath,
|
|
199
|
+
specifierType: 'url',
|
|
200
|
+
loc: {
|
|
201
|
+
filePath: asset.filePath,
|
|
202
|
+
start: decl.source.start,
|
|
203
|
+
end: {
|
|
204
|
+
line: decl.source.start.line,
|
|
205
|
+
column: decl.source.start.column + importPath.length
|
|
206
|
+
}
|
|
145
207
|
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
} // $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
|
|
215
|
+
|
|
153
216
|
|
|
154
217
|
let {
|
|
155
218
|
messages,
|
|
156
219
|
root
|
|
157
|
-
} = await (
|
|
158
|
-
|
|
159
|
-
|
|
220
|
+
} = await postcss(plugins).process(program, config.hydrated);
|
|
221
|
+
asset.setAST({
|
|
222
|
+
type: 'postcss',
|
|
223
|
+
version: '8.2.1',
|
|
224
|
+
program: root.toJSON()
|
|
225
|
+
});
|
|
160
226
|
|
|
161
227
|
for (let msg of messages) {
|
|
162
228
|
if (msg.type === 'dependency') {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
229
|
+
asset.invalidateOnFileChange(msg.file);
|
|
230
|
+
} else if (msg.type === 'dir-dependency') {
|
|
231
|
+
var _msg$glob;
|
|
232
|
+
|
|
233
|
+
let pattern = `${msg.dir}/${(_msg$glob = msg.glob) !== null && _msg$glob !== void 0 ? _msg$glob : '**/*'}`;
|
|
234
|
+
let files = await (0, _utils().glob)(pattern, asset.fs, {
|
|
235
|
+
onlyFiles: true
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
for (let file of files) {
|
|
239
|
+
asset.invalidateOnFileChange(_path().default.normalize(file));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
asset.invalidateOnFileCreate({
|
|
243
|
+
glob: pattern
|
|
167
244
|
});
|
|
168
245
|
}
|
|
169
246
|
}
|
|
170
247
|
|
|
171
248
|
let assets = [asset];
|
|
172
249
|
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
let
|
|
250
|
+
if (cssModules) {
|
|
251
|
+
// $FlowFixMe
|
|
252
|
+
let cssModulesList = Object.entries(cssModules);
|
|
253
|
+
let deps = asset.getDependencies().filter(dep => dep.priority === 'sync');
|
|
254
|
+
let code;
|
|
176
255
|
|
|
177
256
|
if (deps.length > 0) {
|
|
178
257
|
code = `
|
|
179
|
-
module.exports = Object.assign({}, ${deps.map(dep => `require(${JSON.stringify(dep.
|
|
258
|
+
module.exports = Object.assign({}, ${deps.map(dep => `require(${JSON.stringify(dep.specifier)})`).join(', ')}, ${JSON.stringify(cssModules, null, 2)});
|
|
180
259
|
`;
|
|
181
260
|
} else {
|
|
182
|
-
code =
|
|
261
|
+
code = cssModulesList.map( // This syntax enables shaking the invidual statements, so that unused classes don't even exist in JS.
|
|
262
|
+
([className, classNameHashed]) => `module.exports[${JSON.stringify(className)}] = ${JSON.stringify(classNameHashed)};`).join('\n');
|
|
183
263
|
}
|
|
184
264
|
|
|
265
|
+
asset.symbols.ensure();
|
|
266
|
+
|
|
267
|
+
for (let [k, v] of cssModulesList) {
|
|
268
|
+
asset.symbols.set(k, v);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
asset.symbols.set('default', 'default');
|
|
185
272
|
assets.push({
|
|
186
273
|
type: 'js',
|
|
187
|
-
|
|
188
|
-
code
|
|
274
|
+
content: code
|
|
189
275
|
});
|
|
190
276
|
}
|
|
191
277
|
|
|
192
278
|
return assets;
|
|
193
279
|
},
|
|
194
280
|
|
|
195
|
-
generate({
|
|
196
|
-
asset
|
|
281
|
+
async generate({
|
|
282
|
+
asset,
|
|
283
|
+
ast,
|
|
284
|
+
options
|
|
197
285
|
}) {
|
|
198
|
-
|
|
286
|
+
const postcss = await loadPostcss(options, asset.filePath);
|
|
199
287
|
let code = '';
|
|
200
|
-
|
|
201
|
-
_postcss.default.stringify(ast.program, c => {
|
|
288
|
+
postcss.stringify(postcss.fromJSON(ast.program), c => {
|
|
202
289
|
code += c;
|
|
203
290
|
});
|
|
204
|
-
|
|
205
291
|
return {
|
|
206
|
-
code
|
|
292
|
+
content: code
|
|
207
293
|
};
|
|
208
294
|
}
|
|
209
295
|
|
|
@@ -212,14 +298,14 @@ var _default = new _plugin.Transformer({
|
|
|
212
298
|
exports.default = _default;
|
|
213
299
|
|
|
214
300
|
function createLoader(asset, resolve) {
|
|
215
|
-
return class extends _fileSystemLoader.default {
|
|
301
|
+
return class extends _fileSystemLoader().default {
|
|
216
302
|
async fetch(composesPath, relativeTo) {
|
|
217
303
|
let importPath = composesPath.replace(/^["']|["']$/g, '');
|
|
218
304
|
let resolved = await resolve(relativeTo, importPath);
|
|
219
305
|
|
|
220
|
-
let rootRelativePath = _path.default.resolve(_path.default.dirname(relativeTo), resolved);
|
|
306
|
+
let rootRelativePath = _path().default.resolve(_path().default.dirname(relativeTo), resolved);
|
|
221
307
|
|
|
222
|
-
let root = _path.default.resolve('/'); // fixes an issue on windows which is part of the css-modules-loader-core
|
|
308
|
+
let root = _path().default.resolve('/'); // fixes an issue on windows which is part of the css-modules-loader-core
|
|
223
309
|
// see https://github.com/css-modules/css-modules-loader-core/issues/230
|
|
224
310
|
|
|
225
311
|
|
|
@@ -230,7 +316,8 @@ function createLoader(asset, resolve) {
|
|
|
230
316
|
let source = await asset.fs.readFile(resolved, 'utf-8');
|
|
231
317
|
let {
|
|
232
318
|
exportTokens
|
|
233
|
-
} = await this.core.load(source, rootRelativePath, undefined,
|
|
319
|
+
} = await this.core.load(source, rootRelativePath, undefined, // $FlowFixMe[method-unbinding]
|
|
320
|
+
this.fetch.bind(this));
|
|
234
321
|
return exportTokens;
|
|
235
322
|
}
|
|
236
323
|
|
|
@@ -239,4 +326,21 @@ function createLoader(asset, resolve) {
|
|
|
239
326
|
}
|
|
240
327
|
|
|
241
328
|
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function loadPostcss(options, from) {
|
|
332
|
+
return options.packageManager.require('postcss', from, {
|
|
333
|
+
range: _constants.POSTCSS_RANGE,
|
|
334
|
+
saveDev: true,
|
|
335
|
+
shouldAutoInstall: options.shouldAutoInstall
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async function isLegacyCssModule(asset) {
|
|
340
|
+
if (!MODULE_BY_NAME_RE.test(asset.filePath)) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
let code = await asset.getCode();
|
|
345
|
+
return LEGACY_MODULE_RE.test(code);
|
|
242
346
|
}
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.load = load;
|
|
7
|
+
|
|
8
|
+
function _path() {
|
|
9
|
+
const data = _interopRequireDefault(require("path"));
|
|
10
|
+
|
|
11
|
+
_path = function () {
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function _utils() {
|
|
19
|
+
const data = require("@parcel/utils");
|
|
20
|
+
|
|
21
|
+
_utils = function () {
|
|
22
|
+
return data;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return data;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function _nullthrows() {
|
|
29
|
+
const data = _interopRequireDefault(require("nullthrows"));
|
|
30
|
+
|
|
31
|
+
_nullthrows = function () {
|
|
32
|
+
return data;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return data;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function _clone() {
|
|
39
|
+
const data = _interopRequireDefault(require("clone"));
|
|
40
|
+
|
|
41
|
+
_clone = function () {
|
|
42
|
+
return data;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
var _constants = require("./constants");
|
|
49
|
+
|
|
50
|
+
var _loadPlugins = _interopRequireDefault(require("./loadPlugins"));
|
|
51
|
+
|
|
52
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
53
|
+
|
|
54
|
+
async function configHydrator(configFile, config, resolveFrom, options) {
|
|
55
|
+
if (configFile == null) {
|
|
56
|
+
return;
|
|
57
|
+
} // Load the custom config...
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
let modulesConfig;
|
|
61
|
+
let configFilePlugins = (0, _clone().default)(configFile.plugins);
|
|
62
|
+
|
|
63
|
+
if (configFilePlugins != null && typeof configFilePlugins === 'object' && configFilePlugins['postcss-modules'] != null) {
|
|
64
|
+
modulesConfig = configFilePlugins['postcss-modules'];
|
|
65
|
+
delete configFilePlugins['postcss-modules'];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!modulesConfig && configFile.modules) {
|
|
69
|
+
modulesConfig = {};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let plugins = await (0, _loadPlugins.default)(configFilePlugins, (0, _nullthrows().default)(resolveFrom), options); // contents is either:
|
|
73
|
+
// from JSON: { plugins: { 'postcss-foo': { ...opts } } }
|
|
74
|
+
// from JS (v8): { plugins: [ { postcssPlugin: 'postcss-foo', ...visitor callback functions } ]
|
|
75
|
+
// from JS (v7): { plugins: [ [Function: ...] ]
|
|
76
|
+
|
|
77
|
+
let pluginArray = Array.isArray(configFilePlugins) ? configFilePlugins : Object.keys(configFilePlugins);
|
|
78
|
+
|
|
79
|
+
for (let p of pluginArray) {
|
|
80
|
+
if (typeof p === 'string') {
|
|
81
|
+
config.addDevDependency({
|
|
82
|
+
specifier: p,
|
|
83
|
+
resolveFrom: (0, _nullthrows().default)(resolveFrom)
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
raw: configFile,
|
|
90
|
+
hydrated: {
|
|
91
|
+
plugins,
|
|
92
|
+
from: config.searchPath,
|
|
93
|
+
to: config.searchPath,
|
|
94
|
+
modules: modulesConfig
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function load({
|
|
100
|
+
config,
|
|
101
|
+
options,
|
|
102
|
+
logger
|
|
103
|
+
}) {
|
|
104
|
+
if (!config.isSource) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let configFile = await config.getConfig(['.postcssrc', '.postcssrc.json', '.postcssrc.js', 'postcss.config.js'], {
|
|
109
|
+
packageKey: 'postcss'
|
|
110
|
+
});
|
|
111
|
+
let contents = null;
|
|
112
|
+
|
|
113
|
+
if (configFile) {
|
|
114
|
+
config.addDevDependency({
|
|
115
|
+
specifier: 'postcss',
|
|
116
|
+
resolveFrom: config.searchPath,
|
|
117
|
+
range: _constants.POSTCSS_RANGE
|
|
118
|
+
});
|
|
119
|
+
contents = configFile.contents;
|
|
120
|
+
let isDynamic = configFile && _path().default.extname(configFile.filePath) === '.js';
|
|
121
|
+
|
|
122
|
+
if (isDynamic) {
|
|
123
|
+
// We have to invalidate on startup in case the config is non-deterministic,
|
|
124
|
+
// e.g. using unknown environment variables, reading from the filesystem, etc.
|
|
125
|
+
logger.warn({
|
|
126
|
+
message: 'WARNING: Using a JavaScript PostCSS config file means losing out on caching features of Parcel. Use a .postcssrc(.json) file whenever possible.'
|
|
127
|
+
});
|
|
128
|
+
config.invalidateOnStartup(); // Also add the config as a dev dependency so we attempt to reload in watch mode.
|
|
129
|
+
|
|
130
|
+
config.addDevDependency({
|
|
131
|
+
specifier: (0, _utils().relativePath)(_path().default.dirname(config.searchPath), configFile.filePath),
|
|
132
|
+
resolveFrom: config.searchPath
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (typeof contents !== 'object') {
|
|
137
|
+
throw new Error('PostCSS config should be an object.');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (contents.plugins == null || typeof contents.plugins !== 'object' || Object.keys(contents.plugins).length === 0) {
|
|
141
|
+
throw new Error('PostCSS config must have plugins');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return configHydrator(contents, config, configFile === null || configFile === void 0 ? void 0 : configFile.filePath, options);
|
|
146
|
+
}
|
package/lib/loadPlugins.js
CHANGED
|
@@ -7,21 +7,23 @@ exports.default = loadExternalPlugins;
|
|
|
7
7
|
|
|
8
8
|
async function loadExternalPlugins(plugins, relative, options) {
|
|
9
9
|
if (Array.isArray(plugins)) {
|
|
10
|
-
return Promise.all(plugins.map(p => loadPlugin(p, relative, null, options.packageManager)).filter(Boolean));
|
|
10
|
+
return Promise.all(plugins.map(p => loadPlugin(p, relative, null, options.packageManager, options.shouldAutoInstall)).filter(Boolean));
|
|
11
11
|
} else if (typeof plugins === 'object') {
|
|
12
|
-
let mapPlugins = await Promise.all(Object.keys(plugins).map(p => loadPlugin(p, relative, plugins[p], options.packageManager)));
|
|
12
|
+
let mapPlugins = await Promise.all(Object.keys(plugins).map(p => loadPlugin(p, relative, plugins[p], options.packageManager, options.shouldAutoInstall)));
|
|
13
13
|
return mapPlugins.filter(Boolean);
|
|
14
14
|
} else {
|
|
15
15
|
return [];
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
async function loadPlugin(pluginArg, relative, options = {}, packageManager) {
|
|
19
|
+
async function loadPlugin(pluginArg, relative, options = {}, packageManager, shouldAutoInstall) {
|
|
20
20
|
if (typeof pluginArg !== 'string') {
|
|
21
21
|
return pluginArg;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
let plugin = await packageManager.require(pluginArg, relative
|
|
24
|
+
let plugin = await packageManager.require(pluginArg, relative, {
|
|
25
|
+
shouldAutoInstall
|
|
26
|
+
});
|
|
25
27
|
plugin = plugin.default || plugin;
|
|
26
28
|
|
|
27
29
|
if (options != null && typeof options === 'object' && Object.keys(options).length > 0) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parcel/transformer-postcss",
|
|
3
|
-
"version": "2.0.0-nightly.
|
|
3
|
+
"version": "2.0.0-nightly.982+4745cd30",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
|
+
"funding": {
|
|
9
|
+
"type": "opencollective",
|
|
10
|
+
"url": "https://opencollective.com/parcel"
|
|
11
|
+
},
|
|
8
12
|
"repository": {
|
|
9
13
|
"type": "git",
|
|
10
14
|
"url": "https://github.com/parcel-bundler/parcel.git"
|
|
@@ -12,17 +16,22 @@
|
|
|
12
16
|
"main": "lib/PostCSSTransformer.js",
|
|
13
17
|
"source": "src/PostCSSTransformer.js",
|
|
14
18
|
"engines": {
|
|
15
|
-
"node": ">=
|
|
16
|
-
"parcel": "
|
|
19
|
+
"node": ">= 12.0.0",
|
|
20
|
+
"parcel": "2.0.0-nightly.980+4745cd30"
|
|
17
21
|
},
|
|
18
22
|
"dependencies": {
|
|
19
|
-
"@parcel/
|
|
20
|
-
"@parcel/
|
|
23
|
+
"@parcel/hash": "2.2.1-nightly.2605+4745cd30",
|
|
24
|
+
"@parcel/plugin": "2.0.0-nightly.982+4745cd30",
|
|
25
|
+
"@parcel/utils": "2.0.0-nightly.982+4745cd30",
|
|
26
|
+
"clone": "^2.1.1",
|
|
21
27
|
"css-modules-loader-core": "^1.1.0",
|
|
22
28
|
"nullthrows": "^1.1.1",
|
|
23
|
-
"postcss": "^
|
|
24
|
-
"postcss-value-parser": "^
|
|
25
|
-
"semver": "^5.
|
|
29
|
+
"postcss-modules": "^3.2.2",
|
|
30
|
+
"postcss-value-parser": "^4.1.0",
|
|
31
|
+
"semver": "^5.7.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"postcss": "^8.3.0"
|
|
26
35
|
},
|
|
27
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "4745cd3023f8d5a5adcf9e565d5b82d1418dc262"
|
|
28
37
|
}
|
|
@@ -1,207 +1,200 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
|
|
3
|
-
import type {FilePath, MutableAsset} from '@parcel/types';
|
|
3
|
+
import type {FilePath, Asset, MutableAsset, PluginOptions} from '@parcel/types';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {hashString} from '@parcel/hash';
|
|
6
|
+
import {glob} from '@parcel/utils';
|
|
6
7
|
import {Transformer} from '@parcel/plugin';
|
|
7
8
|
import FileSystemLoader from 'css-modules-loader-core/lib/file-system-loader';
|
|
8
9
|
import nullthrows from 'nullthrows';
|
|
9
10
|
import path from 'path';
|
|
10
|
-
import postcss from 'postcss';
|
|
11
11
|
import semver from 'semver';
|
|
12
12
|
import valueParser from 'postcss-value-parser';
|
|
13
|
+
import postcssModules from 'postcss-modules';
|
|
14
|
+
import typeof * as Postcss from 'postcss';
|
|
13
15
|
|
|
14
|
-
import
|
|
16
|
+
import {load} from './loadConfig';
|
|
17
|
+
import {POSTCSS_RANGE} from './constants';
|
|
15
18
|
|
|
16
19
|
const COMPOSES_RE = /composes:.+from\s*("|').*("|')\s*;?/;
|
|
17
20
|
const FROM_IMPORT_RE = /.+from\s*(?:"|')(.*)(?:"|')\s*;?/;
|
|
21
|
+
const LEGACY_MODULE_RE = /@value|(:global|:local)(?!\s*\()/i;
|
|
18
22
|
const MODULE_BY_NAME_RE = /\.module\./;
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
to: FilePath,
|
|
24
|
-
...
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export default new Transformer({
|
|
28
|
-
async getConfig({asset, resolve, options}): Promise<?ParcelPostCSSConfig> {
|
|
29
|
-
let configFile: mixed = await asset.getConfig(
|
|
30
|
-
['.postcssrc', '.postcssrc.json', '.postcssrc.js', 'postcss.config.js'],
|
|
31
|
-
{packageKey: 'postcss'},
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
// Use a basic, modules-only PostCSS config if the file opts in by a name
|
|
35
|
-
// like foo.module.css
|
|
36
|
-
if (configFile == null && asset.filePath.match(MODULE_BY_NAME_RE)) {
|
|
37
|
-
configFile = {
|
|
38
|
-
plugins: {
|
|
39
|
-
'postcss-modules': {},
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (configFile == null) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (typeof configFile !== 'object') {
|
|
49
|
-
throw new Error('PostCSS config should be an object.');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
configFile.plugins == null ||
|
|
54
|
-
typeof configFile.plugins !== 'object' ||
|
|
55
|
-
Object.keys(configFile.plugins) === 0
|
|
56
|
-
) {
|
|
57
|
-
throw new Error('PostCSS config must have plugins');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let originalModulesConfig;
|
|
61
|
-
let configFilePlugins = configFile.plugins;
|
|
62
|
-
if (
|
|
63
|
-
configFilePlugins != null &&
|
|
64
|
-
typeof configFilePlugins === 'object' &&
|
|
65
|
-
configFilePlugins['postcss-modules'] != null
|
|
66
|
-
) {
|
|
67
|
-
originalModulesConfig = configFilePlugins['postcss-modules'];
|
|
68
|
-
// $FlowFixMe
|
|
69
|
-
delete configFilePlugins['postcss-modules'];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let plugins = await loadPlugins(configFilePlugins, asset.filePath, options);
|
|
73
|
-
|
|
74
|
-
if (originalModulesConfig || configFile.modules) {
|
|
75
|
-
let postcssModules = await options.packageManager.require(
|
|
76
|
-
'postcss-modules',
|
|
77
|
-
asset.filePath,
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
plugins.push(
|
|
81
|
-
postcssModules({
|
|
82
|
-
getJSON: (filename, json) => (asset.meta.cssModules = json),
|
|
83
|
-
Loader: createLoader(asset, resolve),
|
|
84
|
-
generateScopedName: (name, filename, css) =>
|
|
85
|
-
`_${name}_${md5FromString(filename + css).substr(0, 5)}`,
|
|
86
|
-
...originalModulesConfig,
|
|
87
|
-
}),
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
plugins,
|
|
93
|
-
from: asset.filePath,
|
|
94
|
-
to: asset.filePath,
|
|
95
|
-
};
|
|
24
|
+
export default (new Transformer({
|
|
25
|
+
loadConfig({config, options, logger}) {
|
|
26
|
+
return load({config, options, logger});
|
|
96
27
|
},
|
|
97
28
|
|
|
98
29
|
canReuseAST({ast}) {
|
|
99
|
-
return
|
|
30
|
+
return (
|
|
31
|
+
ast.type === 'postcss' && semver.satisfies(ast.version, POSTCSS_RANGE)
|
|
32
|
+
);
|
|
100
33
|
},
|
|
101
34
|
|
|
102
|
-
async parse({asset, config}) {
|
|
103
|
-
|
|
35
|
+
async parse({asset, config, options}) {
|
|
36
|
+
let isLegacy = await isLegacyCssModule(asset);
|
|
37
|
+
if (!config && !isLegacy) {
|
|
104
38
|
return;
|
|
105
39
|
}
|
|
106
40
|
|
|
41
|
+
const postcss = await loadPostcss(options, asset.filePath);
|
|
42
|
+
|
|
107
43
|
return {
|
|
108
44
|
type: 'postcss',
|
|
109
|
-
version: '
|
|
110
|
-
program: postcss
|
|
111
|
-
|
|
112
|
-
|
|
45
|
+
version: '8.2.1',
|
|
46
|
+
program: postcss
|
|
47
|
+
.parse(await asset.getCode(), {
|
|
48
|
+
from: asset.filePath,
|
|
49
|
+
})
|
|
50
|
+
.toJSON(),
|
|
113
51
|
};
|
|
114
52
|
},
|
|
115
53
|
|
|
116
|
-
async transform({asset, config}) {
|
|
54
|
+
async transform({asset, config, options, resolve}) {
|
|
55
|
+
asset.type = 'css';
|
|
56
|
+
let isLegacy = await isLegacyCssModule(asset);
|
|
57
|
+
if (isLegacy && !config) {
|
|
58
|
+
config = {
|
|
59
|
+
hydrated: {
|
|
60
|
+
plugins: [],
|
|
61
|
+
from: asset.filePath,
|
|
62
|
+
to: asset.filePath,
|
|
63
|
+
modules: {},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// TODO: warning?
|
|
68
|
+
}
|
|
69
|
+
|
|
117
70
|
if (!config) {
|
|
118
71
|
return [asset];
|
|
119
72
|
}
|
|
120
73
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
74
|
+
const postcss: Postcss = await loadPostcss(options, asset.filePath);
|
|
75
|
+
let ast = nullthrows(await asset.getAST());
|
|
76
|
+
let program = postcss.fromJSON(ast.program);
|
|
77
|
+
|
|
78
|
+
let plugins = [...config.hydrated.plugins];
|
|
79
|
+
let cssModules: ?{|[string]: string|} = null;
|
|
80
|
+
if (config.hydrated.modules) {
|
|
81
|
+
asset.meta.cssModulesCompiled = true;
|
|
82
|
+
plugins.push(
|
|
83
|
+
postcssModules({
|
|
84
|
+
getJSON: (filename, json) => (cssModules = json),
|
|
85
|
+
Loader: createLoader(asset, resolve),
|
|
86
|
+
generateScopedName: (name, filename) =>
|
|
87
|
+
`${name}_${hashString(
|
|
88
|
+
path.relative(options.projectRoot, filename),
|
|
89
|
+
).substr(0, 6)}`,
|
|
90
|
+
...config.hydrated.modules,
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
let code = asset.isASTDirty() ? null : await asset.getCode();
|
|
95
|
+
if (code == null || COMPOSES_RE.test(code)) {
|
|
96
|
+
program.walkDecls(decl => {
|
|
97
|
+
let [, importPath] = FROM_IMPORT_RE.exec(decl.value) || [];
|
|
98
|
+
if (decl.prop === 'composes' && importPath != null) {
|
|
99
|
+
let parsed = valueParser(decl.value);
|
|
100
|
+
|
|
101
|
+
parsed.walk(node => {
|
|
102
|
+
if (node.type === 'string') {
|
|
103
|
+
asset.addDependency({
|
|
104
|
+
specifier: importPath,
|
|
105
|
+
specifierType: 'url',
|
|
106
|
+
loc: {
|
|
107
|
+
filePath: asset.filePath,
|
|
108
|
+
start: decl.source.start,
|
|
109
|
+
end: {
|
|
110
|
+
line: decl.source.start.line,
|
|
111
|
+
column: decl.source.start.column + importPath.length,
|
|
112
|
+
},
|
|
138
113
|
},
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
145
120
|
}
|
|
146
121
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
122
|
+
// $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
|
|
123
|
+
let {messages, root} = await postcss(plugins).process(
|
|
124
|
+
program,
|
|
125
|
+
config.hydrated,
|
|
150
126
|
);
|
|
151
|
-
|
|
152
|
-
|
|
127
|
+
asset.setAST({
|
|
128
|
+
type: 'postcss',
|
|
129
|
+
version: '8.2.1',
|
|
130
|
+
program: root.toJSON(),
|
|
131
|
+
});
|
|
153
132
|
for (let msg of messages) {
|
|
154
133
|
if (msg.type === 'dependency') {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
asset.addIncludedFile({
|
|
164
|
-
filePath: msg.file,
|
|
165
|
-
});
|
|
134
|
+
asset.invalidateOnFileChange(msg.file);
|
|
135
|
+
} else if (msg.type === 'dir-dependency') {
|
|
136
|
+
let pattern = `${msg.dir}/${msg.glob ?? '**/*'}`;
|
|
137
|
+
let files = await glob(pattern, asset.fs, {onlyFiles: true});
|
|
138
|
+
for (let file of files) {
|
|
139
|
+
asset.invalidateOnFileChange(path.normalize(file));
|
|
140
|
+
}
|
|
141
|
+
asset.invalidateOnFileCreate({glob: pattern});
|
|
166
142
|
}
|
|
167
143
|
}
|
|
168
144
|
|
|
169
145
|
let assets = [asset];
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
let
|
|
146
|
+
if (cssModules) {
|
|
147
|
+
// $FlowFixMe
|
|
148
|
+
let cssModulesList = (Object.entries(cssModules): Array<
|
|
149
|
+
[string, string],
|
|
150
|
+
>);
|
|
151
|
+
let deps = asset.getDependencies().filter(dep => dep.priority === 'sync');
|
|
152
|
+
let code: string;
|
|
173
153
|
if (deps.length > 0) {
|
|
174
154
|
code = `
|
|
175
155
|
module.exports = Object.assign({}, ${deps
|
|
176
|
-
.map(dep => `require(${JSON.stringify(dep.
|
|
177
|
-
.join(', ')}, ${
|
|
156
|
+
.map(dep => `require(${JSON.stringify(dep.specifier)})`)
|
|
157
|
+
.join(', ')}, ${JSON.stringify(cssModules, null, 2)});
|
|
178
158
|
`;
|
|
179
159
|
} else {
|
|
180
|
-
code =
|
|
160
|
+
code = cssModulesList
|
|
161
|
+
.map(
|
|
162
|
+
// This syntax enables shaking the invidual statements, so that unused classes don't even exist in JS.
|
|
163
|
+
([className, classNameHashed]) =>
|
|
164
|
+
`module.exports[${JSON.stringify(className)}] = ${JSON.stringify(
|
|
165
|
+
classNameHashed,
|
|
166
|
+
)};`,
|
|
167
|
+
)
|
|
168
|
+
.join('\n');
|
|
181
169
|
}
|
|
182
170
|
|
|
171
|
+
asset.symbols.ensure();
|
|
172
|
+
for (let [k, v] of cssModulesList) {
|
|
173
|
+
asset.symbols.set(k, v);
|
|
174
|
+
}
|
|
175
|
+
asset.symbols.set('default', 'default');
|
|
176
|
+
|
|
183
177
|
assets.push({
|
|
184
178
|
type: 'js',
|
|
185
|
-
|
|
186
|
-
code,
|
|
179
|
+
content: code,
|
|
187
180
|
});
|
|
188
181
|
}
|
|
189
182
|
return assets;
|
|
190
183
|
},
|
|
191
184
|
|
|
192
|
-
generate({asset}) {
|
|
193
|
-
|
|
185
|
+
async generate({asset, ast, options}) {
|
|
186
|
+
const postcss: Postcss = await loadPostcss(options, asset.filePath);
|
|
194
187
|
|
|
195
188
|
let code = '';
|
|
196
|
-
postcss.stringify(ast.program, c => {
|
|
189
|
+
postcss.stringify(postcss.fromJSON(ast.program), c => {
|
|
197
190
|
code += c;
|
|
198
191
|
});
|
|
199
192
|
|
|
200
193
|
return {
|
|
201
|
-
code,
|
|
194
|
+
content: code,
|
|
202
195
|
};
|
|
203
196
|
},
|
|
204
|
-
});
|
|
197
|
+
}): Transformer);
|
|
205
198
|
|
|
206
199
|
function createLoader(
|
|
207
200
|
asset: MutableAsset,
|
|
@@ -224,6 +217,7 @@ function createLoader(
|
|
|
224
217
|
source,
|
|
225
218
|
rootRelativePath,
|
|
226
219
|
undefined,
|
|
220
|
+
// $FlowFixMe[method-unbinding]
|
|
227
221
|
this.fetch.bind(this),
|
|
228
222
|
);
|
|
229
223
|
return exportTokens;
|
|
@@ -234,3 +228,20 @@ function createLoader(
|
|
|
234
228
|
}
|
|
235
229
|
};
|
|
236
230
|
}
|
|
231
|
+
|
|
232
|
+
function loadPostcss(options: PluginOptions, from: FilePath): Promise<Postcss> {
|
|
233
|
+
return options.packageManager.require('postcss', from, {
|
|
234
|
+
range: POSTCSS_RANGE,
|
|
235
|
+
saveDev: true,
|
|
236
|
+
shouldAutoInstall: options.shouldAutoInstall,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function isLegacyCssModule(asset: Asset | MutableAsset) {
|
|
241
|
+
if (!MODULE_BY_NAME_RE.test(asset.filePath)) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let code = await asset.getCode();
|
|
246
|
+
return LEGACY_MODULE_RE.test(code);
|
|
247
|
+
}
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type {
|
|
3
|
+
Config,
|
|
4
|
+
FilePath,
|
|
5
|
+
PluginOptions,
|
|
6
|
+
PluginLogger,
|
|
7
|
+
} from '@parcel/types';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import {relativePath} from '@parcel/utils';
|
|
10
|
+
import nullthrows from 'nullthrows';
|
|
11
|
+
import clone from 'clone';
|
|
12
|
+
import {POSTCSS_RANGE} from './constants';
|
|
13
|
+
|
|
14
|
+
import loadExternalPlugins from './loadPlugins';
|
|
15
|
+
|
|
16
|
+
type ConfigResult = {|
|
|
17
|
+
raw: any,
|
|
18
|
+
hydrated: {|
|
|
19
|
+
plugins: Array<any>,
|
|
20
|
+
from: FilePath,
|
|
21
|
+
to: FilePath,
|
|
22
|
+
modules: any,
|
|
23
|
+
|},
|
|
24
|
+
|};
|
|
25
|
+
|
|
26
|
+
async function configHydrator(
|
|
27
|
+
configFile: any,
|
|
28
|
+
config: Config,
|
|
29
|
+
resolveFrom: ?FilePath,
|
|
30
|
+
options: PluginOptions,
|
|
31
|
+
): Promise<?ConfigResult> {
|
|
32
|
+
if (configFile == null) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Load the custom config...
|
|
37
|
+
let modulesConfig;
|
|
38
|
+
let configFilePlugins = clone(configFile.plugins);
|
|
39
|
+
if (
|
|
40
|
+
configFilePlugins != null &&
|
|
41
|
+
typeof configFilePlugins === 'object' &&
|
|
42
|
+
configFilePlugins['postcss-modules'] != null
|
|
43
|
+
) {
|
|
44
|
+
modulesConfig = configFilePlugins['postcss-modules'];
|
|
45
|
+
delete configFilePlugins['postcss-modules'];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!modulesConfig && configFile.modules) {
|
|
49
|
+
modulesConfig = {};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let plugins = await loadExternalPlugins(
|
|
53
|
+
configFilePlugins,
|
|
54
|
+
nullthrows(resolveFrom),
|
|
55
|
+
options,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// contents is either:
|
|
59
|
+
// from JSON: { plugins: { 'postcss-foo': { ...opts } } }
|
|
60
|
+
// from JS (v8): { plugins: [ { postcssPlugin: 'postcss-foo', ...visitor callback functions } ]
|
|
61
|
+
// from JS (v7): { plugins: [ [Function: ...] ]
|
|
62
|
+
let pluginArray = Array.isArray(configFilePlugins)
|
|
63
|
+
? configFilePlugins
|
|
64
|
+
: Object.keys(configFilePlugins);
|
|
65
|
+
for (let p of pluginArray) {
|
|
66
|
+
if (typeof p === 'string') {
|
|
67
|
+
config.addDevDependency({
|
|
68
|
+
specifier: p,
|
|
69
|
+
resolveFrom: nullthrows(resolveFrom),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
raw: configFile,
|
|
76
|
+
hydrated: {
|
|
77
|
+
plugins,
|
|
78
|
+
from: config.searchPath,
|
|
79
|
+
to: config.searchPath,
|
|
80
|
+
modules: modulesConfig,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function load({
|
|
86
|
+
config,
|
|
87
|
+
options,
|
|
88
|
+
logger,
|
|
89
|
+
}: {|
|
|
90
|
+
config: Config,
|
|
91
|
+
options: PluginOptions,
|
|
92
|
+
logger: PluginLogger,
|
|
93
|
+
|}): Promise<?ConfigResult> {
|
|
94
|
+
if (!config.isSource) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let configFile: any = await config.getConfig(
|
|
99
|
+
['.postcssrc', '.postcssrc.json', '.postcssrc.js', 'postcss.config.js'],
|
|
100
|
+
{packageKey: 'postcss'},
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
let contents = null;
|
|
104
|
+
if (configFile) {
|
|
105
|
+
config.addDevDependency({
|
|
106
|
+
specifier: 'postcss',
|
|
107
|
+
resolveFrom: config.searchPath,
|
|
108
|
+
range: POSTCSS_RANGE,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
contents = configFile.contents;
|
|
112
|
+
let isDynamic = configFile && path.extname(configFile.filePath) === '.js';
|
|
113
|
+
if (isDynamic) {
|
|
114
|
+
// We have to invalidate on startup in case the config is non-deterministic,
|
|
115
|
+
// e.g. using unknown environment variables, reading from the filesystem, etc.
|
|
116
|
+
logger.warn({
|
|
117
|
+
message:
|
|
118
|
+
'WARNING: Using a JavaScript PostCSS config file means losing out on caching features of Parcel. Use a .postcssrc(.json) file whenever possible.',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
config.invalidateOnStartup();
|
|
122
|
+
|
|
123
|
+
// Also add the config as a dev dependency so we attempt to reload in watch mode.
|
|
124
|
+
config.addDevDependency({
|
|
125
|
+
specifier: relativePath(
|
|
126
|
+
path.dirname(config.searchPath),
|
|
127
|
+
configFile.filePath,
|
|
128
|
+
),
|
|
129
|
+
resolveFrom: config.searchPath,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (typeof contents !== 'object') {
|
|
134
|
+
throw new Error('PostCSS config should be an object.');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (
|
|
138
|
+
contents.plugins == null ||
|
|
139
|
+
typeof contents.plugins !== 'object' ||
|
|
140
|
+
Object.keys(contents.plugins).length === 0
|
|
141
|
+
) {
|
|
142
|
+
throw new Error('PostCSS config must have plugins');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return configHydrator(contents, config, configFile?.filePath, options);
|
|
147
|
+
}
|
package/src/loadPlugins.js
CHANGED
|
@@ -11,14 +11,28 @@ export default async function loadExternalPlugins(
|
|
|
11
11
|
if (Array.isArray(plugins)) {
|
|
12
12
|
return Promise.all(
|
|
13
13
|
plugins
|
|
14
|
-
.map(p =>
|
|
14
|
+
.map(p =>
|
|
15
|
+
loadPlugin(
|
|
16
|
+
p,
|
|
17
|
+
relative,
|
|
18
|
+
null,
|
|
19
|
+
options.packageManager,
|
|
20
|
+
options.shouldAutoInstall,
|
|
21
|
+
),
|
|
22
|
+
)
|
|
15
23
|
.filter(Boolean),
|
|
16
24
|
);
|
|
17
25
|
} else if (typeof plugins === 'object') {
|
|
18
26
|
let _plugins = plugins;
|
|
19
27
|
let mapPlugins = await Promise.all(
|
|
20
28
|
Object.keys(plugins).map(p =>
|
|
21
|
-
loadPlugin(
|
|
29
|
+
loadPlugin(
|
|
30
|
+
p,
|
|
31
|
+
relative,
|
|
32
|
+
_plugins[p],
|
|
33
|
+
options.packageManager,
|
|
34
|
+
options.shouldAutoInstall,
|
|
35
|
+
),
|
|
22
36
|
),
|
|
23
37
|
);
|
|
24
38
|
return mapPlugins.filter(Boolean);
|
|
@@ -32,12 +46,15 @@ async function loadPlugin(
|
|
|
32
46
|
relative: FilePath,
|
|
33
47
|
options: mixed = {},
|
|
34
48
|
packageManager: PackageManager,
|
|
49
|
+
shouldAutoInstall: boolean,
|
|
35
50
|
): mixed {
|
|
36
51
|
if (typeof pluginArg !== 'string') {
|
|
37
52
|
return pluginArg;
|
|
38
53
|
}
|
|
39
54
|
|
|
40
|
-
let plugin = await packageManager.require(pluginArg, relative
|
|
55
|
+
let plugin = await packageManager.require(pluginArg, relative, {
|
|
56
|
+
shouldAutoInstall,
|
|
57
|
+
});
|
|
41
58
|
plugin = plugin.default || plugin;
|
|
42
59
|
|
|
43
60
|
if (
|