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