@parcel/transformer-postcss 2.0.0-nightly.102 → 2.0.0-nightly.1027
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 +290 -120
- package/lib/constants.js +8 -0
- package/lib/loadConfig.js +210 -0
- package/lib/loadPlugins.js +6 -4
- package/package.json +19 -10
- package/src/PostCSSTransformer.js +234 -136
- package/src/constants.js +3 -0
- package/src/loadConfig.js +215 -0
- package/src/loadPlugins.js +20 -3
|
@@ -1,212 +1,292 @@
|
|
|
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
|
|
14
|
+
import {load} from './loadConfig';
|
|
15
|
+
import {POSTCSS_RANGE} from './constants';
|
|
16
|
+
import {md, generateJSONCodeHighlights} from '@parcel/diagnostic';
|
|
15
17
|
|
|
16
18
|
const COMPOSES_RE = /composes:.+from\s*("|').*("|')\s*;?/;
|
|
17
19
|
const FROM_IMPORT_RE = /.+from\s*(?:"|')(.*)(?:"|')\s*;?/;
|
|
20
|
+
const LEGACY_MODULE_RE = /@value|(:global|:local)(?!\s*\()/i;
|
|
18
21
|
const MODULE_BY_NAME_RE = /\.module\./;
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
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'},
|
|
23
|
+
export default (new Transformer({
|
|
24
|
+
loadConfig({config, options, logger}) {
|
|
25
|
+
return load({config, options, logger});
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
canReuseAST({ast}) {
|
|
29
|
+
return (
|
|
30
|
+
ast.type === 'postcss' && semver.satisfies(ast.version, POSTCSS_RANGE)
|
|
32
31
|
);
|
|
32
|
+
},
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
async parse({asset, config, options}) {
|
|
35
|
+
let isLegacy = await isLegacyCssModule(asset);
|
|
36
|
+
if (!config && !isLegacy) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const postcss = await loadPostcss(options, asset.filePath);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
type: 'postcss',
|
|
44
|
+
version: '8.2.1',
|
|
45
|
+
program: postcss
|
|
46
|
+
.parse(await asset.getCode(), {
|
|
47
|
+
from: asset.filePath,
|
|
48
|
+
})
|
|
49
|
+
.toJSON(),
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async transform({asset, config, options, resolve, logger}) {
|
|
54
|
+
asset.type = 'css';
|
|
55
|
+
let isLegacy = await isLegacyCssModule(asset);
|
|
56
|
+
if (isLegacy && !config) {
|
|
57
|
+
config = {
|
|
58
|
+
raw: {},
|
|
59
|
+
filePath: '',
|
|
60
|
+
hydrated: {
|
|
61
|
+
plugins: [],
|
|
62
|
+
from: asset.filePath,
|
|
63
|
+
to: asset.filePath,
|
|
64
|
+
modules: {},
|
|
40
65
|
},
|
|
41
66
|
};
|
|
42
|
-
}
|
|
43
67
|
|
|
44
|
-
|
|
45
|
-
return;
|
|
68
|
+
// TODO: warning?
|
|
46
69
|
}
|
|
47
70
|
|
|
48
|
-
if (
|
|
49
|
-
|
|
71
|
+
if (!config) {
|
|
72
|
+
return [asset];
|
|
50
73
|
}
|
|
51
74
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Object.keys(configFile.plugins) === 0
|
|
56
|
-
) {
|
|
57
|
-
throw new Error('PostCSS config must have plugins');
|
|
58
|
-
}
|
|
75
|
+
const postcss: Postcss = await loadPostcss(options, asset.filePath);
|
|
76
|
+
let ast = nullthrows(await asset.getAST());
|
|
77
|
+
let program = postcss.fromJSON(ast.program);
|
|
59
78
|
|
|
60
|
-
let
|
|
61
|
-
let
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
typeof configFilePlugins === 'object' &&
|
|
65
|
-
configFilePlugins['postcss-modules'] != null
|
|
66
|
-
) {
|
|
67
|
-
originalModulesConfig = configFilePlugins['postcss-modules'];
|
|
68
|
-
// $FlowFixMe
|
|
69
|
-
delete configFilePlugins['postcss-modules'];
|
|
70
|
-
}
|
|
79
|
+
let plugins = [...config.hydrated.plugins];
|
|
80
|
+
let cssModules: ?{|[string]: string|} = null;
|
|
81
|
+
if (config.hydrated.modules) {
|
|
82
|
+
asset.meta.cssModulesCompiled = true;
|
|
71
83
|
|
|
72
|
-
|
|
84
|
+
let code = asset.isASTDirty() ? null : await asset.getCode();
|
|
85
|
+
if (
|
|
86
|
+
Object.keys(config.hydrated.modules).length === 0 &&
|
|
87
|
+
code &&
|
|
88
|
+
!isLegacy &&
|
|
89
|
+
!LEGACY_MODULE_RE.test(code)
|
|
90
|
+
) {
|
|
91
|
+
let filename = path.basename(config.filePath);
|
|
92
|
+
let message;
|
|
93
|
+
let configKey;
|
|
94
|
+
let hint;
|
|
95
|
+
if (config.raw.modules) {
|
|
96
|
+
message = md`The "modules" option in __${filename}__ can be replaced with configuration for @parcel/transformer-css to improve build performance.`;
|
|
97
|
+
configKey = '/modules';
|
|
98
|
+
hint = md`Remove the "modules" option from __${filename}__`;
|
|
99
|
+
} else {
|
|
100
|
+
message = md`The "postcss-modules" plugin in __${filename}__ can be replaced with configuration for @parcel/transformer-css to improve build performance.`;
|
|
101
|
+
configKey = '/plugins/postcss-modules';
|
|
102
|
+
hint = md`Remove the "postcss-modules" plugin from __${filename}__`;
|
|
103
|
+
}
|
|
73
104
|
|
|
74
|
-
|
|
105
|
+
let hints = [
|
|
106
|
+
'Enable the "cssModules" option for "@parcel/transformer-css" in your package.json',
|
|
107
|
+
];
|
|
108
|
+
if (plugins.length === 0) {
|
|
109
|
+
message += md` Since there are no other plugins, __${filename}__ can be deleted safely.`;
|
|
110
|
+
hints.push(md`Delete __${filename}__`);
|
|
111
|
+
} else {
|
|
112
|
+
hints.push(hint);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let codeFrames;
|
|
116
|
+
if (path.extname(filename) !== '.js') {
|
|
117
|
+
let contents = await asset.fs.readFile(config.filePath, 'utf8');
|
|
118
|
+
codeFrames = [
|
|
119
|
+
{
|
|
120
|
+
language: 'json',
|
|
121
|
+
filePath: config.filePath,
|
|
122
|
+
code: contents,
|
|
123
|
+
codeHighlights: generateJSONCodeHighlights(contents, [
|
|
124
|
+
{
|
|
125
|
+
key: configKey,
|
|
126
|
+
type: 'key',
|
|
127
|
+
},
|
|
128
|
+
]),
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
} else {
|
|
132
|
+
codeFrames = [
|
|
133
|
+
{
|
|
134
|
+
filePath: config.filePath,
|
|
135
|
+
codeHighlights: [
|
|
136
|
+
{
|
|
137
|
+
start: {line: 1, column: 1},
|
|
138
|
+
end: {line: 1, column: 1},
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
logger.warn({
|
|
146
|
+
message,
|
|
147
|
+
hints,
|
|
148
|
+
documentationURL:
|
|
149
|
+
'https://parceljs.org/languages/css/#enabling-css-modules-globally',
|
|
150
|
+
codeFrames,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// TODO: should this be resolved from the project root?
|
|
75
155
|
let postcssModules = await options.packageManager.require(
|
|
76
156
|
'postcss-modules',
|
|
77
157
|
asset.filePath,
|
|
158
|
+
{
|
|
159
|
+
range: '^4.3.0',
|
|
160
|
+
saveDev: true,
|
|
161
|
+
shouldAutoInstall: options.shouldAutoInstall,
|
|
162
|
+
},
|
|
78
163
|
);
|
|
79
164
|
|
|
80
165
|
plugins.push(
|
|
81
166
|
postcssModules({
|
|
82
|
-
getJSON: (filename, json) => (
|
|
83
|
-
Loader: createLoader(asset, resolve),
|
|
84
|
-
generateScopedName: (name, filename
|
|
85
|
-
|
|
86
|
-
|
|
167
|
+
getJSON: (filename, json) => (cssModules = json),
|
|
168
|
+
Loader: await createLoader(asset, resolve, options),
|
|
169
|
+
generateScopedName: (name, filename) =>
|
|
170
|
+
`${name}_${hashString(
|
|
171
|
+
path.relative(options.projectRoot, filename),
|
|
172
|
+
).substr(0, 6)}`,
|
|
173
|
+
...config.hydrated.modules,
|
|
87
174
|
}),
|
|
88
175
|
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
plugins,
|
|
93
|
-
from: asset.filePath,
|
|
94
|
-
to: asset.filePath,
|
|
95
|
-
};
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
canReuseAST({ast}) {
|
|
99
|
-
return ast.type === 'postcss' && semver.satisfies(ast.version, '^7.0.0');
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
async parse({asset, config}) {
|
|
103
|
-
if (!config) {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
176
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}),
|
|
113
|
-
};
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
async transform({asset, config}) {
|
|
117
|
-
if (!config) {
|
|
118
|
-
return [asset];
|
|
119
|
-
}
|
|
177
|
+
if (code == null || COMPOSES_RE.test(code)) {
|
|
178
|
+
program.walkDecls(decl => {
|
|
179
|
+
let [, importPath] = FROM_IMPORT_RE.exec(decl.value) || [];
|
|
180
|
+
if (decl.prop === 'composes' && importPath != null) {
|
|
181
|
+
let parsed = valueParser(decl.value);
|
|
120
182
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
filePath: importPath,
|
|
134
|
-
start: decl.source.start,
|
|
135
|
-
end: {
|
|
136
|
-
line: decl.source.start.line,
|
|
137
|
-
column: decl.source.start.column + importPath.length,
|
|
183
|
+
parsed.walk(node => {
|
|
184
|
+
if (node.type === 'string') {
|
|
185
|
+
asset.addDependency({
|
|
186
|
+
specifier: importPath,
|
|
187
|
+
specifierType: 'url',
|
|
188
|
+
loc: {
|
|
189
|
+
filePath: asset.filePath,
|
|
190
|
+
start: decl.source.start,
|
|
191
|
+
end: {
|
|
192
|
+
line: decl.source.start.line,
|
|
193
|
+
column: decl.source.start.column + importPath.length,
|
|
194
|
+
},
|
|
138
195
|
},
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
145
202
|
}
|
|
146
203
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
204
|
+
// $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
|
|
205
|
+
let {messages, root} = await postcss(plugins).process(
|
|
206
|
+
program,
|
|
207
|
+
config.hydrated,
|
|
150
208
|
);
|
|
151
|
-
|
|
152
|
-
|
|
209
|
+
asset.setAST({
|
|
210
|
+
type: 'postcss',
|
|
211
|
+
version: '8.2.1',
|
|
212
|
+
program: root.toJSON(),
|
|
213
|
+
});
|
|
153
214
|
for (let msg of messages) {
|
|
154
215
|
if (msg.type === 'dependency') {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
asset.addIncludedFile({
|
|
164
|
-
filePath: msg.file,
|
|
165
|
-
});
|
|
216
|
+
asset.invalidateOnFileChange(msg.file);
|
|
217
|
+
} else if (msg.type === 'dir-dependency') {
|
|
218
|
+
let pattern = `${msg.dir}/${msg.glob ?? '**/*'}`;
|
|
219
|
+
let files = await glob(pattern, asset.fs, {onlyFiles: true});
|
|
220
|
+
for (let file of files) {
|
|
221
|
+
asset.invalidateOnFileChange(path.normalize(file));
|
|
222
|
+
}
|
|
223
|
+
asset.invalidateOnFileCreate({glob: pattern});
|
|
166
224
|
}
|
|
167
225
|
}
|
|
168
226
|
|
|
169
227
|
let assets = [asset];
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
let
|
|
228
|
+
if (cssModules) {
|
|
229
|
+
// $FlowFixMe
|
|
230
|
+
let cssModulesList = (Object.entries(cssModules): Array<
|
|
231
|
+
[string, string],
|
|
232
|
+
>);
|
|
233
|
+
let deps = asset.getDependencies().filter(dep => dep.priority === 'sync');
|
|
234
|
+
let code: string;
|
|
173
235
|
if (deps.length > 0) {
|
|
174
236
|
code = `
|
|
175
237
|
module.exports = Object.assign({}, ${deps
|
|
176
|
-
.map(dep => `require(${JSON.stringify(dep.
|
|
177
|
-
.join(', ')}, ${
|
|
238
|
+
.map(dep => `require(${JSON.stringify(dep.specifier)})`)
|
|
239
|
+
.join(', ')}, ${JSON.stringify(cssModules, null, 2)});
|
|
178
240
|
`;
|
|
179
241
|
} else {
|
|
180
|
-
code =
|
|
242
|
+
code = cssModulesList
|
|
243
|
+
.map(
|
|
244
|
+
// This syntax enables shaking the invidual statements, so that unused classes don't even exist in JS.
|
|
245
|
+
([className, classNameHashed]) =>
|
|
246
|
+
`module.exports[${JSON.stringify(className)}] = ${JSON.stringify(
|
|
247
|
+
classNameHashed,
|
|
248
|
+
)};`,
|
|
249
|
+
)
|
|
250
|
+
.join('\n');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
asset.symbols.ensure();
|
|
254
|
+
for (let [k, v] of cssModulesList) {
|
|
255
|
+
asset.symbols.set(k, v);
|
|
181
256
|
}
|
|
257
|
+
asset.symbols.set('default', 'default');
|
|
182
258
|
|
|
183
259
|
assets.push({
|
|
184
260
|
type: 'js',
|
|
185
|
-
|
|
186
|
-
code,
|
|
261
|
+
content: code,
|
|
187
262
|
});
|
|
188
263
|
}
|
|
189
264
|
return assets;
|
|
190
265
|
},
|
|
191
266
|
|
|
192
|
-
generate({asset}) {
|
|
193
|
-
|
|
267
|
+
async generate({asset, ast, options}) {
|
|
268
|
+
const postcss: Postcss = await loadPostcss(options, asset.filePath);
|
|
194
269
|
|
|
195
270
|
let code = '';
|
|
196
|
-
postcss.stringify(ast.program, c => {
|
|
271
|
+
postcss.stringify(postcss.fromJSON(ast.program), c => {
|
|
197
272
|
code += c;
|
|
198
273
|
});
|
|
199
274
|
|
|
200
275
|
return {
|
|
201
|
-
code,
|
|
276
|
+
content: code,
|
|
202
277
|
};
|
|
203
278
|
},
|
|
204
|
-
});
|
|
279
|
+
}): Transformer);
|
|
205
280
|
|
|
206
|
-
function createLoader(
|
|
281
|
+
async function createLoader(
|
|
207
282
|
asset: MutableAsset,
|
|
208
283
|
resolve: (from: FilePath, to: string) => Promise<FilePath>,
|
|
284
|
+
options: PluginOptions,
|
|
209
285
|
) {
|
|
286
|
+
let {default: FileSystemLoader} = await options.packageManager.require(
|
|
287
|
+
'postcss-modules/build/css-loader-core/loader',
|
|
288
|
+
asset.filePath,
|
|
289
|
+
);
|
|
210
290
|
return class ParcelFileSystemLoader extends FileSystemLoader {
|
|
211
291
|
async fetch(composesPath, relativeTo) {
|
|
212
292
|
let importPath = composesPath.replace(/^["']|["']$/g, '');
|
|
@@ -224,6 +304,7 @@ function createLoader(
|
|
|
224
304
|
source,
|
|
225
305
|
rootRelativePath,
|
|
226
306
|
undefined,
|
|
307
|
+
// $FlowFixMe[method-unbinding]
|
|
227
308
|
this.fetch.bind(this),
|
|
228
309
|
);
|
|
229
310
|
return exportTokens;
|
|
@@ -234,3 +315,20 @@ function createLoader(
|
|
|
234
315
|
}
|
|
235
316
|
};
|
|
236
317
|
}
|
|
318
|
+
|
|
319
|
+
function loadPostcss(options: PluginOptions, from: FilePath): Promise<Postcss> {
|
|
320
|
+
return options.packageManager.require('postcss', from, {
|
|
321
|
+
range: POSTCSS_RANGE,
|
|
322
|
+
saveDev: true,
|
|
323
|
+
shouldAutoInstall: options.shouldAutoInstall,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async function isLegacyCssModule(asset: Asset | MutableAsset) {
|
|
328
|
+
if (!MODULE_BY_NAME_RE.test(asset.filePath)) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
let code = await asset.getCode();
|
|
333
|
+
return LEGACY_MODULE_RE.test(code);
|
|
334
|
+
}
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
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 {md, generateJSONCodeHighlights} from '@parcel/diagnostic';
|
|
11
|
+
import nullthrows from 'nullthrows';
|
|
12
|
+
import clone from 'clone';
|
|
13
|
+
import {POSTCSS_RANGE} from './constants';
|
|
14
|
+
|
|
15
|
+
import loadExternalPlugins from './loadPlugins';
|
|
16
|
+
|
|
17
|
+
type ConfigResult = {|
|
|
18
|
+
raw: any,
|
|
19
|
+
filePath: string,
|
|
20
|
+
hydrated: {|
|
|
21
|
+
plugins: Array<any>,
|
|
22
|
+
from: FilePath,
|
|
23
|
+
to: FilePath,
|
|
24
|
+
modules: any,
|
|
25
|
+
|},
|
|
26
|
+
|};
|
|
27
|
+
|
|
28
|
+
async function configHydrator(
|
|
29
|
+
configFile: any,
|
|
30
|
+
config: Config,
|
|
31
|
+
resolveFrom: FilePath,
|
|
32
|
+
options: PluginOptions,
|
|
33
|
+
logger: PluginLogger,
|
|
34
|
+
): Promise<?ConfigResult> {
|
|
35
|
+
if (configFile == null) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Load the custom config...
|
|
40
|
+
let modulesConfig;
|
|
41
|
+
let configFilePlugins = clone(configFile.plugins);
|
|
42
|
+
if (
|
|
43
|
+
configFilePlugins != null &&
|
|
44
|
+
typeof configFilePlugins === 'object' &&
|
|
45
|
+
configFilePlugins['postcss-modules'] != null
|
|
46
|
+
) {
|
|
47
|
+
modulesConfig = configFilePlugins['postcss-modules'];
|
|
48
|
+
delete configFilePlugins['postcss-modules'];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!modulesConfig && configFile.modules) {
|
|
52
|
+
modulesConfig = {};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let plugins = await loadExternalPlugins(
|
|
56
|
+
configFilePlugins,
|
|
57
|
+
nullthrows(resolveFrom),
|
|
58
|
+
options,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// contents is either:
|
|
62
|
+
// from JSON: { plugins: { 'postcss-foo': { ...opts } } }
|
|
63
|
+
// from JS (v8): { plugins: [ { postcssPlugin: 'postcss-foo', ...visitor callback functions } ]
|
|
64
|
+
// from JS (v7): { plugins: [ [Function: ...] ]
|
|
65
|
+
let pluginArray = Array.isArray(configFilePlugins)
|
|
66
|
+
? configFilePlugins
|
|
67
|
+
: Object.keys(configFilePlugins);
|
|
68
|
+
for (let p of pluginArray) {
|
|
69
|
+
if (typeof p === 'string') {
|
|
70
|
+
config.addDevDependency({
|
|
71
|
+
specifier: p,
|
|
72
|
+
resolveFrom: nullthrows(resolveFrom),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let redundantPlugins = pluginArray.filter(
|
|
78
|
+
p => p === 'autoprefixer' || p === 'postcss-preset-env',
|
|
79
|
+
);
|
|
80
|
+
if (redundantPlugins.length > 0) {
|
|
81
|
+
let filename = path.basename(resolveFrom);
|
|
82
|
+
let message;
|
|
83
|
+
let hints = [];
|
|
84
|
+
if (redundantPlugins.length === pluginArray.length) {
|
|
85
|
+
message = md`Parcel includes CSS transpilation and vendor prefixing by default. PostCSS config __${filename}__ contains only redundant plugins. Deleting it may significantly improve build performance.`;
|
|
86
|
+
hints.push(md`Delete __${filename}__`);
|
|
87
|
+
} else {
|
|
88
|
+
message = md`Parcel includes CSS transpilation and vendor prefixing by default. PostCSS config __${filename}__ contains the following redundant plugins: ${[
|
|
89
|
+
...redundantPlugins,
|
|
90
|
+
].map(p =>
|
|
91
|
+
md.underline(p),
|
|
92
|
+
)}. Removing these may improve build performance.`;
|
|
93
|
+
hints.push(md`Remove the above plugins from __${filename}__`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let codeFrames;
|
|
97
|
+
if (path.extname(filename) !== '.js') {
|
|
98
|
+
let contents = await options.inputFS.readFile(resolveFrom, 'utf8');
|
|
99
|
+
codeFrames = [
|
|
100
|
+
{
|
|
101
|
+
language: 'json',
|
|
102
|
+
filePath: resolveFrom,
|
|
103
|
+
code: contents,
|
|
104
|
+
codeHighlights: generateJSONCodeHighlights(
|
|
105
|
+
contents,
|
|
106
|
+
redundantPlugins.map(plugin => ({
|
|
107
|
+
key: `/plugins/${plugin}`,
|
|
108
|
+
type: 'key',
|
|
109
|
+
})),
|
|
110
|
+
),
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
} else {
|
|
114
|
+
codeFrames = [
|
|
115
|
+
{
|
|
116
|
+
filePath: resolveFrom,
|
|
117
|
+
codeHighlights: [
|
|
118
|
+
{
|
|
119
|
+
start: {line: 1, column: 1},
|
|
120
|
+
end: {line: 1, column: 1},
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
logger.warn({
|
|
128
|
+
message,
|
|
129
|
+
hints,
|
|
130
|
+
documentationURL: 'https://parceljs.org/languages/css/#default-plugins',
|
|
131
|
+
codeFrames,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
raw: configFile,
|
|
137
|
+
filePath: resolveFrom,
|
|
138
|
+
hydrated: {
|
|
139
|
+
plugins,
|
|
140
|
+
from: config.searchPath,
|
|
141
|
+
to: config.searchPath,
|
|
142
|
+
modules: modulesConfig,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function load({
|
|
148
|
+
config,
|
|
149
|
+
options,
|
|
150
|
+
logger,
|
|
151
|
+
}: {|
|
|
152
|
+
config: Config,
|
|
153
|
+
options: PluginOptions,
|
|
154
|
+
logger: PluginLogger,
|
|
155
|
+
|}): Promise<?ConfigResult> {
|
|
156
|
+
if (!config.isSource) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let configFile: any = await config.getConfig(
|
|
161
|
+
['.postcssrc', '.postcssrc.json', '.postcssrc.js', 'postcss.config.js'],
|
|
162
|
+
{packageKey: 'postcss'},
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
let contents = null;
|
|
166
|
+
if (configFile) {
|
|
167
|
+
config.addDevDependency({
|
|
168
|
+
specifier: 'postcss',
|
|
169
|
+
resolveFrom: config.searchPath,
|
|
170
|
+
range: POSTCSS_RANGE,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
contents = configFile.contents;
|
|
174
|
+
let isDynamic = configFile && path.extname(configFile.filePath) === '.js';
|
|
175
|
+
if (isDynamic) {
|
|
176
|
+
// We have to invalidate on startup in case the config is non-deterministic,
|
|
177
|
+
// e.g. using unknown environment variables, reading from the filesystem, etc.
|
|
178
|
+
logger.warn({
|
|
179
|
+
message:
|
|
180
|
+
'WARNING: Using a JavaScript PostCSS config file means losing out on caching features of Parcel. Use a .postcssrc(.json) file whenever possible.',
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
config.invalidateOnStartup();
|
|
184
|
+
|
|
185
|
+
// Also add the config as a dev dependency so we attempt to reload in watch mode.
|
|
186
|
+
config.addDevDependency({
|
|
187
|
+
specifier: relativePath(
|
|
188
|
+
path.dirname(config.searchPath),
|
|
189
|
+
configFile.filePath,
|
|
190
|
+
),
|
|
191
|
+
resolveFrom: config.searchPath,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (typeof contents !== 'object') {
|
|
196
|
+
throw new Error('PostCSS config should be an object.');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (
|
|
200
|
+
contents.plugins == null ||
|
|
201
|
+
typeof contents.plugins !== 'object' ||
|
|
202
|
+
Object.keys(contents.plugins).length === 0
|
|
203
|
+
) {
|
|
204
|
+
throw new Error('PostCSS config must have plugins');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return configHydrator(
|
|
209
|
+
contents,
|
|
210
|
+
config,
|
|
211
|
+
configFile?.filePath,
|
|
212
|
+
options,
|
|
213
|
+
logger,
|
|
214
|
+
);
|
|
215
|
+
}
|