@parcel/transformer-css 2.0.0-beta.3.1 → 2.0.0-nightly.1006
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/CSSTransformer.js +204 -52
- package/package.json +13 -9
- package/src/CSSTransformer.js +213 -56
package/lib/CSSTransformer.js
CHANGED
|
@@ -5,6 +5,16 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
+
function _hash() {
|
|
9
|
+
const data = require("@parcel/hash");
|
|
10
|
+
|
|
11
|
+
_hash = function () {
|
|
12
|
+
return data;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
|
|
8
18
|
function _sourceMap() {
|
|
9
19
|
const data = _interopRequireDefault(require("@parcel/source-map"));
|
|
10
20
|
|
|
@@ -75,10 +85,23 @@ function _semver() {
|
|
|
75
85
|
return data;
|
|
76
86
|
}
|
|
77
87
|
|
|
88
|
+
function _path() {
|
|
89
|
+
const data = _interopRequireDefault(require("path"));
|
|
90
|
+
|
|
91
|
+
_path = function () {
|
|
92
|
+
return data;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
|
|
78
98
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
79
99
|
|
|
80
|
-
const URL_RE = /url\s*\(
|
|
100
|
+
const URL_RE = /url\s*\(/;
|
|
81
101
|
const IMPORT_RE = /@import/;
|
|
102
|
+
const COMPOSES_RE = /composes:.+from\s*("|').*("|')\s*;?/;
|
|
103
|
+
const FROM_IMPORT_RE = /.+from\s*(?:"|')(.*)(?:"|')\s*;?/;
|
|
104
|
+
const MODULE_BY_NAME_RE = /\.module\./;
|
|
82
105
|
|
|
83
106
|
function canHaveDependencies(filePath, code) {
|
|
84
107
|
return !/\.css$/.test(filePath) || IMPORT_RE.test(code) || URL_RE.test(code);
|
|
@@ -100,13 +123,15 @@ var _default = new (_plugin().Transformer)({
|
|
|
100
123
|
// to be filled in later. When the CSS transformer runs, it would pick that up and try to
|
|
101
124
|
// resolve a dependency for the id which obviously doesn't exist. Also, it's faster to do
|
|
102
125
|
// it this way since the resulting CSS doesn't need to be re-parsed.
|
|
103
|
-
|
|
126
|
+
let isCSSModule = asset.meta.cssModulesCompiled !== true && MODULE_BY_NAME_RE.test(asset.filePath);
|
|
127
|
+
|
|
128
|
+
if (asset.meta.hasDependencies === false && !isCSSModule) {
|
|
104
129
|
return null;
|
|
105
130
|
}
|
|
106
131
|
|
|
107
132
|
let code = await asset.getCode();
|
|
108
133
|
|
|
109
|
-
if (code != null && !canHaveDependencies(asset.filePath, code)) {
|
|
134
|
+
if (code != null && !canHaveDependencies(asset.filePath, code) && !isCSSModule) {
|
|
110
135
|
return null;
|
|
111
136
|
}
|
|
112
137
|
|
|
@@ -120,10 +145,13 @@ var _default = new (_plugin().Transformer)({
|
|
|
120
145
|
},
|
|
121
146
|
|
|
122
147
|
async transform({
|
|
123
|
-
asset
|
|
148
|
+
asset,
|
|
149
|
+
resolve,
|
|
150
|
+
options
|
|
124
151
|
}) {
|
|
125
152
|
// Normalize the asset's environment so that properties that only affect JS don't cause CSS to be duplicated.
|
|
126
153
|
// For example, with ESModule and CommonJS targets, only a single shared CSS bundle should be produced.
|
|
154
|
+
let env = asset.env;
|
|
127
155
|
asset.setEnvironment({
|
|
128
156
|
context: 'browser',
|
|
129
157
|
engines: {
|
|
@@ -131,28 +159,33 @@ var _default = new (_plugin().Transformer)({
|
|
|
131
159
|
},
|
|
132
160
|
shouldOptimize: asset.env.shouldOptimize,
|
|
133
161
|
sourceMap: asset.env.sourceMap
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (asset.isSplittable == null) {
|
|
138
|
-
asset.isSplittable = true;
|
|
139
|
-
} // Check for `hasDependencies` being false here as well, as it's possible
|
|
162
|
+
});
|
|
163
|
+
let isCSSModule = asset.meta.cssModulesCompiled !== true && MODULE_BY_NAME_RE.test(asset.filePath); // Check for `hasDependencies` being false here as well, as it's possible
|
|
140
164
|
// another transformer (such as PostCSSTransformer) has already parsed an
|
|
141
165
|
// ast and CSSTransformer's parse was never called.
|
|
142
166
|
|
|
143
|
-
|
|
144
167
|
let ast = await asset.getAST();
|
|
145
168
|
|
|
146
|
-
if (!ast || asset.meta.hasDependencies === false) {
|
|
169
|
+
if (!ast || asset.meta.hasDependencies === false && !isCSSModule) {
|
|
147
170
|
return [asset];
|
|
148
171
|
}
|
|
149
172
|
|
|
150
173
|
let program = _postcss().default.fromJSON(ast.program);
|
|
151
174
|
|
|
175
|
+
let assets = [asset];
|
|
176
|
+
|
|
177
|
+
if (isCSSModule) {
|
|
178
|
+
assets = await compileCSSModules(asset, env, program, resolve, options);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (asset.meta.hasDependencies === false) {
|
|
182
|
+
return assets;
|
|
183
|
+
}
|
|
184
|
+
|
|
152
185
|
let originalSourceMap = await asset.getMap();
|
|
153
186
|
|
|
154
|
-
let createLoc = (start, specifier, lineOffset, colOffset) => {
|
|
155
|
-
let loc = (0, _utils().createDependencyLocation)(start, specifier, lineOffset, colOffset);
|
|
187
|
+
let createLoc = (start, specifier, lineOffset, colOffset, o) => {
|
|
188
|
+
let loc = (0, _utils().createDependencyLocation)(start, specifier, lineOffset, colOffset, o);
|
|
156
189
|
|
|
157
190
|
if (originalSourceMap) {
|
|
158
191
|
loc = (0, _utils().remapSourceLocation)(loc, originalSourceMap);
|
|
@@ -165,43 +198,40 @@ var _default = new (_plugin().Transformer)({
|
|
|
165
198
|
program.walkAtRules('import', rule => {
|
|
166
199
|
let params = (0, _postcssValueParser().default)(rule.params);
|
|
167
200
|
let [name, ...media] = params.nodes;
|
|
168
|
-
let
|
|
201
|
+
let specifier;
|
|
169
202
|
|
|
170
203
|
if (name.type === 'function' && name.value === 'url' && name.nodes.length) {
|
|
171
204
|
name = name.nodes[0];
|
|
172
205
|
}
|
|
173
206
|
|
|
174
|
-
|
|
207
|
+
specifier = name.value;
|
|
175
208
|
|
|
176
|
-
if (!
|
|
209
|
+
if (!specifier) {
|
|
177
210
|
throw new Error('Could not find import name for ' + String(rule));
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
asset.addDependency(dep);
|
|
203
|
-
rule.remove(); // }
|
|
204
|
-
}
|
|
211
|
+
} // If this came from an inline <style> tag, don't inline the imported file. Replace with the correct URL instead.
|
|
212
|
+
// TODO: run CSSPackager on inline style tags.
|
|
213
|
+
// let inlineHTML =
|
|
214
|
+
// this.options.rendition && this.options.rendition.inlineHTML;
|
|
215
|
+
// if (inlineHTML) {
|
|
216
|
+
// name.value = asset.addURLDependency(dep, {loc: rule.source.start});
|
|
217
|
+
// rule.params = params.toString();
|
|
218
|
+
// } else {
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
media = _postcssValueParser().default.stringify(media).trim();
|
|
222
|
+
let dep = {
|
|
223
|
+
specifier,
|
|
224
|
+
specifierType: 'url',
|
|
225
|
+
// Offset by 8 as it does not include `@import `
|
|
226
|
+
loc: createLoc((0, _nullthrows().default)(rule.source.start), specifier, 0, 8),
|
|
227
|
+
meta: {
|
|
228
|
+
// For the glob resolver to distinguish between `@import` and other URL dependencies.
|
|
229
|
+
isCSSImport: true,
|
|
230
|
+
media
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
asset.addDependency(dep);
|
|
234
|
+
rule.remove(); // }
|
|
205
235
|
|
|
206
236
|
isDirty = true;
|
|
207
237
|
});
|
|
@@ -212,12 +242,15 @@ var _default = new (_plugin().Transformer)({
|
|
|
212
242
|
parsed.walk(node => {
|
|
213
243
|
if (node.type === 'function' && node.value === 'url' && node.nodes.length > 0 && !node.nodes[0].value.startsWith('#') // IE's `behavior: url(#default#VML)`
|
|
214
244
|
) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
245
|
+
let urlNode = node.nodes[0];
|
|
246
|
+
let url = asset.addURLDependency(urlNode.value, {
|
|
247
|
+
loc: decl.source && decl.source.start && createLoc(decl.source.start, urlNode.value, 0, decl.source.start.offset + urlNode.sourceIndex + 1, 0)
|
|
248
|
+
});
|
|
249
|
+
isDeclDirty = urlNode.value !== url;
|
|
250
|
+
urlNode.type = 'string';
|
|
251
|
+
urlNode.quote = '"';
|
|
252
|
+
urlNode.value = url;
|
|
253
|
+
}
|
|
221
254
|
});
|
|
222
255
|
|
|
223
256
|
if (isDeclDirty) {
|
|
@@ -233,7 +266,7 @@ var _default = new (_plugin().Transformer)({
|
|
|
233
266
|
});
|
|
234
267
|
}
|
|
235
268
|
|
|
236
|
-
return
|
|
269
|
+
return assets;
|
|
237
270
|
},
|
|
238
271
|
|
|
239
272
|
async generate({
|
|
@@ -275,4 +308,123 @@ var _default = new (_plugin().Transformer)({
|
|
|
275
308
|
|
|
276
309
|
});
|
|
277
310
|
|
|
278
|
-
exports.default = _default;
|
|
311
|
+
exports.default = _default;
|
|
312
|
+
|
|
313
|
+
async function compileCSSModules(asset, env, program, resolve, options) {
|
|
314
|
+
let cssModules;
|
|
315
|
+
let code = asset.isASTDirty() ? null : await asset.getCode();
|
|
316
|
+
|
|
317
|
+
if (code == null || COMPOSES_RE.test(code)) {
|
|
318
|
+
program.walkDecls(decl => {
|
|
319
|
+
let [, importPath] = FROM_IMPORT_RE.exec(decl.value) || [];
|
|
320
|
+
|
|
321
|
+
if (decl.prop === 'composes' && importPath != null) {
|
|
322
|
+
let parsed = (0, _postcssValueParser().default)(decl.value);
|
|
323
|
+
let start = decl.source.start;
|
|
324
|
+
parsed.walk(node => {
|
|
325
|
+
if (node.type === 'string') {
|
|
326
|
+
asset.addDependency({
|
|
327
|
+
specifier: importPath,
|
|
328
|
+
specifierType: 'url',
|
|
329
|
+
loc: start ? {
|
|
330
|
+
filePath: asset.filePath,
|
|
331
|
+
start,
|
|
332
|
+
end: {
|
|
333
|
+
line: start.line,
|
|
334
|
+
column: start.column + importPath.length
|
|
335
|
+
}
|
|
336
|
+
} : undefined
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
let postcssModules = await options.packageManager.require('postcss-modules', asset.filePath, {
|
|
345
|
+
range: '^4.3.0',
|
|
346
|
+
saveDev: true,
|
|
347
|
+
shouldAutoInstall: options.shouldAutoInstall
|
|
348
|
+
});
|
|
349
|
+
let {
|
|
350
|
+
root
|
|
351
|
+
} = await (0, _postcss().default)([postcssModules({
|
|
352
|
+
getJSON: (filename, json) => cssModules = json,
|
|
353
|
+
Loader: await createLoader(asset, resolve, options),
|
|
354
|
+
generateScopedName: (name, filename) => `${name}_${(0, _hash().hashString)(_path().default.relative(options.projectRoot, filename)).substr(0, 6)}`
|
|
355
|
+
})]).process(program, {
|
|
356
|
+
from: asset.filePath,
|
|
357
|
+
to: asset.filePath
|
|
358
|
+
});
|
|
359
|
+
asset.setAST({
|
|
360
|
+
type: 'postcss',
|
|
361
|
+
version: '8.2.1',
|
|
362
|
+
program: root.toJSON()
|
|
363
|
+
});
|
|
364
|
+
let assets = [asset];
|
|
365
|
+
|
|
366
|
+
if (cssModules) {
|
|
367
|
+
// $FlowFixMe
|
|
368
|
+
let cssModulesList = Object.entries(cssModules);
|
|
369
|
+
let deps = asset.getDependencies().filter(dep => dep.priority === 'sync');
|
|
370
|
+
let code;
|
|
371
|
+
|
|
372
|
+
if (deps.length > 0) {
|
|
373
|
+
code = `
|
|
374
|
+
module.exports = Object.assign({}, ${deps.map(dep => `require(${JSON.stringify(dep.specifier)})`).join(', ')}, ${JSON.stringify(cssModules, null, 2)});
|
|
375
|
+
`;
|
|
376
|
+
} else {
|
|
377
|
+
code = cssModulesList.map( // This syntax enables shaking the invidual statements, so that unused classes don't even exist in JS.
|
|
378
|
+
([className, classNameHashed]) => `module.exports[${JSON.stringify(className)}] = ${JSON.stringify(classNameHashed)};`).join('\n');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
asset.symbols.ensure();
|
|
382
|
+
|
|
383
|
+
for (let [k, v] of cssModulesList) {
|
|
384
|
+
asset.symbols.set(k, v);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
asset.symbols.set('default', 'default');
|
|
388
|
+
assets.push({
|
|
389
|
+
type: 'js',
|
|
390
|
+
content: code,
|
|
391
|
+
env
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return assets;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function createLoader(asset, resolve, options) {
|
|
399
|
+
let {
|
|
400
|
+
default: FileSystemLoader
|
|
401
|
+
} = await options.packageManager.require('postcss-modules/build/css-loader-core/loader', asset.filePath);
|
|
402
|
+
return class extends FileSystemLoader {
|
|
403
|
+
async fetch(composesPath, relativeTo) {
|
|
404
|
+
let importPath = composesPath.replace(/^["']|["']$/g, '');
|
|
405
|
+
let resolved = await resolve(relativeTo, importPath);
|
|
406
|
+
|
|
407
|
+
let rootRelativePath = _path().default.resolve(_path().default.dirname(relativeTo), resolved);
|
|
408
|
+
|
|
409
|
+
let root = _path().default.resolve('/'); // fixes an issue on windows which is part of the css-modules-loader-core
|
|
410
|
+
// see https://github.com/css-modules/css-modules-loader-core/issues/230
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
if (rootRelativePath.startsWith(root)) {
|
|
414
|
+
rootRelativePath = rootRelativePath.substr(root.length);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
let source = await asset.fs.readFile(resolved, 'utf-8');
|
|
418
|
+
let {
|
|
419
|
+
exportTokens
|
|
420
|
+
} = await this.core.load(source, rootRelativePath, undefined, // $FlowFixMe[method-unbinding]
|
|
421
|
+
this.fetch.bind(this));
|
|
422
|
+
return exportTokens;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
get finalSource() {
|
|
426
|
+
return '';
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
};
|
|
430
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parcel/transformer-css",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-nightly.1006+18b038d5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -17,16 +17,20 @@
|
|
|
17
17
|
"source": "src/CSSTransformer.js",
|
|
18
18
|
"engines": {
|
|
19
19
|
"node": ">= 12.0.0",
|
|
20
|
-
"parcel": "
|
|
20
|
+
"parcel": "2.0.0-nightly.1004+18b038d5"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@parcel/
|
|
24
|
-
"@parcel/
|
|
25
|
-
"@parcel/
|
|
23
|
+
"@parcel/hash": "2.3.3-nightly.2629+18b038d5",
|
|
24
|
+
"@parcel/plugin": "2.0.0-nightly.1006+18b038d5",
|
|
25
|
+
"@parcel/source-map": "^2.0.0",
|
|
26
|
+
"@parcel/utils": "2.0.0-nightly.1006+18b038d5",
|
|
26
27
|
"nullthrows": "^1.1.1",
|
|
27
|
-
"postcss": "^8.
|
|
28
|
-
"postcss-value-parser": "^4.
|
|
29
|
-
"semver": "^5.
|
|
28
|
+
"postcss": "^8.4.5",
|
|
29
|
+
"postcss-value-parser": "^4.2.0",
|
|
30
|
+
"semver": "^5.7.1"
|
|
30
31
|
},
|
|
31
|
-
"
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"postcss-modules": "^4.3.0"
|
|
34
|
+
},
|
|
35
|
+
"gitHead": "18b038d57043965afb31077db1f46dadb11a6a78"
|
|
32
36
|
}
|
package/src/CSSTransformer.js
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
|
|
3
3
|
import type {Root} from 'postcss';
|
|
4
|
-
import type {FilePath} from '@parcel/types';
|
|
4
|
+
import type {FilePath, MutableAsset, PluginOptions} from '@parcel/types';
|
|
5
5
|
|
|
6
|
+
import {hashString} from '@parcel/hash';
|
|
6
7
|
import SourceMap from '@parcel/source-map';
|
|
7
8
|
import {Transformer} from '@parcel/plugin';
|
|
8
|
-
import {
|
|
9
|
-
createDependencyLocation,
|
|
10
|
-
isURL,
|
|
11
|
-
remapSourceLocation,
|
|
12
|
-
} from '@parcel/utils';
|
|
9
|
+
import {createDependencyLocation, remapSourceLocation} from '@parcel/utils';
|
|
13
10
|
import postcss from 'postcss';
|
|
14
11
|
import nullthrows from 'nullthrows';
|
|
15
12
|
import valueParser from 'postcss-value-parser';
|
|
16
13
|
import semver from 'semver';
|
|
14
|
+
import path from 'path';
|
|
17
15
|
|
|
18
|
-
const URL_RE = /url\s*\(
|
|
16
|
+
const URL_RE = /url\s*\(/;
|
|
19
17
|
const IMPORT_RE = /@import/;
|
|
18
|
+
const COMPOSES_RE = /composes:.+from\s*("|').*("|')\s*;?/;
|
|
19
|
+
const FROM_IMPORT_RE = /.+from\s*(?:"|')(.*)(?:"|')\s*;?/;
|
|
20
|
+
const MODULE_BY_NAME_RE = /\.module\./;
|
|
20
21
|
|
|
21
22
|
function canHaveDependencies(filePath: FilePath, code: string) {
|
|
22
23
|
return !/\.css$/.test(filePath) || IMPORT_RE.test(code) || URL_RE.test(code);
|
|
@@ -34,12 +35,19 @@ export default (new Transformer({
|
|
|
34
35
|
// to be filled in later. When the CSS transformer runs, it would pick that up and try to
|
|
35
36
|
// resolve a dependency for the id which obviously doesn't exist. Also, it's faster to do
|
|
36
37
|
// it this way since the resulting CSS doesn't need to be re-parsed.
|
|
37
|
-
|
|
38
|
+
let isCSSModule =
|
|
39
|
+
asset.meta.cssModulesCompiled !== true &&
|
|
40
|
+
MODULE_BY_NAME_RE.test(asset.filePath);
|
|
41
|
+
if (asset.meta.hasDependencies === false && !isCSSModule) {
|
|
38
42
|
return null;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
let code = await asset.getCode();
|
|
42
|
-
if (
|
|
46
|
+
if (
|
|
47
|
+
code != null &&
|
|
48
|
+
!canHaveDependencies(asset.filePath, code) &&
|
|
49
|
+
!isCSSModule
|
|
50
|
+
) {
|
|
43
51
|
return null;
|
|
44
52
|
}
|
|
45
53
|
|
|
@@ -54,9 +62,10 @@ export default (new Transformer({
|
|
|
54
62
|
};
|
|
55
63
|
},
|
|
56
64
|
|
|
57
|
-
async transform({asset}) {
|
|
65
|
+
async transform({asset, resolve, options}) {
|
|
58
66
|
// Normalize the asset's environment so that properties that only affect JS don't cause CSS to be duplicated.
|
|
59
67
|
// For example, with ESModule and CommonJS targets, only a single shared CSS bundle should be produced.
|
|
68
|
+
let env = asset.env;
|
|
60
69
|
asset.setEnvironment({
|
|
61
70
|
context: 'browser',
|
|
62
71
|
engines: {
|
|
@@ -66,28 +75,36 @@ export default (new Transformer({
|
|
|
66
75
|
sourceMap: asset.env.sourceMap,
|
|
67
76
|
});
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
asset.isSplittable = true;
|
|
73
|
-
}
|
|
78
|
+
let isCSSModule =
|
|
79
|
+
asset.meta.cssModulesCompiled !== true &&
|
|
80
|
+
MODULE_BY_NAME_RE.test(asset.filePath);
|
|
74
81
|
|
|
75
82
|
// Check for `hasDependencies` being false here as well, as it's possible
|
|
76
83
|
// another transformer (such as PostCSSTransformer) has already parsed an
|
|
77
84
|
// ast and CSSTransformer's parse was never called.
|
|
78
85
|
let ast = await asset.getAST();
|
|
79
|
-
if (!ast || asset.meta.hasDependencies === false) {
|
|
86
|
+
if (!ast || (asset.meta.hasDependencies === false && !isCSSModule)) {
|
|
80
87
|
return [asset];
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
let program: Root = postcss.fromJSON(ast.program);
|
|
91
|
+
let assets = [asset];
|
|
92
|
+
if (isCSSModule) {
|
|
93
|
+
assets = await compileCSSModules(asset, env, program, resolve, options);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (asset.meta.hasDependencies === false) {
|
|
97
|
+
return assets;
|
|
98
|
+
}
|
|
99
|
+
|
|
84
100
|
let originalSourceMap = await asset.getMap();
|
|
85
|
-
let createLoc = (start, specifier, lineOffset, colOffset) => {
|
|
101
|
+
let createLoc = (start, specifier, lineOffset, colOffset, o) => {
|
|
86
102
|
let loc = createDependencyLocation(
|
|
87
103
|
start,
|
|
88
104
|
specifier,
|
|
89
105
|
lineOffset,
|
|
90
106
|
colOffset,
|
|
107
|
+
o,
|
|
91
108
|
);
|
|
92
109
|
if (originalSourceMap) {
|
|
93
110
|
loc = remapSourceLocation(loc, originalSourceMap);
|
|
@@ -99,7 +116,7 @@ export default (new Transformer({
|
|
|
99
116
|
program.walkAtRules('import', rule => {
|
|
100
117
|
let params = valueParser(rule.params);
|
|
101
118
|
let [name, ...media] = params.nodes;
|
|
102
|
-
let
|
|
119
|
+
let specifier;
|
|
103
120
|
if (
|
|
104
121
|
name.type === 'function' &&
|
|
105
122
|
name.value === 'url' &&
|
|
@@ -108,38 +125,35 @@ export default (new Transformer({
|
|
|
108
125
|
name = name.nodes[0];
|
|
109
126
|
}
|
|
110
127
|
|
|
111
|
-
|
|
128
|
+
specifier = name.value;
|
|
112
129
|
|
|
113
|
-
if (!
|
|
130
|
+
if (!specifier) {
|
|
114
131
|
throw new Error('Could not find import name for ' + String(rule));
|
|
115
132
|
}
|
|
116
133
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
rule.remove();
|
|
141
|
-
// }
|
|
142
|
-
}
|
|
134
|
+
// If this came from an inline <style> tag, don't inline the imported file. Replace with the correct URL instead.
|
|
135
|
+
// TODO: run CSSPackager on inline style tags.
|
|
136
|
+
// let inlineHTML =
|
|
137
|
+
// this.options.rendition && this.options.rendition.inlineHTML;
|
|
138
|
+
// if (inlineHTML) {
|
|
139
|
+
// name.value = asset.addURLDependency(dep, {loc: rule.source.start});
|
|
140
|
+
// rule.params = params.toString();
|
|
141
|
+
// } else {
|
|
142
|
+
media = valueParser.stringify(media).trim();
|
|
143
|
+
let dep = {
|
|
144
|
+
specifier,
|
|
145
|
+
specifierType: 'url',
|
|
146
|
+
// Offset by 8 as it does not include `@import `
|
|
147
|
+
loc: createLoc(nullthrows(rule.source.start), specifier, 0, 8),
|
|
148
|
+
meta: {
|
|
149
|
+
// For the glob resolver to distinguish between `@import` and other URL dependencies.
|
|
150
|
+
isCSSImport: true,
|
|
151
|
+
media,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
asset.addDependency(dep);
|
|
155
|
+
rule.remove();
|
|
156
|
+
// }
|
|
143
157
|
isDirty = true;
|
|
144
158
|
});
|
|
145
159
|
|
|
@@ -155,16 +169,23 @@ export default (new Transformer({
|
|
|
155
169
|
node.nodes.length > 0 &&
|
|
156
170
|
!node.nodes[0].value.startsWith('#') // IE's `behavior: url(#default#VML)`
|
|
157
171
|
) {
|
|
158
|
-
let
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
let urlNode = node.nodes[0];
|
|
173
|
+
let url = asset.addURLDependency(urlNode.value, {
|
|
174
|
+
loc:
|
|
175
|
+
decl.source &&
|
|
176
|
+
decl.source.start &&
|
|
177
|
+
createLoc(
|
|
178
|
+
decl.source.start,
|
|
179
|
+
urlNode.value,
|
|
180
|
+
0,
|
|
181
|
+
decl.source.start.offset + urlNode.sourceIndex + 1,
|
|
182
|
+
0,
|
|
183
|
+
),
|
|
165
184
|
});
|
|
166
|
-
isDeclDirty =
|
|
167
|
-
|
|
185
|
+
isDeclDirty = urlNode.value !== url;
|
|
186
|
+
urlNode.type = 'string';
|
|
187
|
+
urlNode.quote = '"';
|
|
188
|
+
urlNode.value = url;
|
|
168
189
|
}
|
|
169
190
|
});
|
|
170
191
|
|
|
@@ -182,7 +203,7 @@ export default (new Transformer({
|
|
|
182
203
|
});
|
|
183
204
|
}
|
|
184
205
|
|
|
185
|
-
return
|
|
206
|
+
return assets;
|
|
186
207
|
},
|
|
187
208
|
|
|
188
209
|
async generate({asset, ast, options}) {
|
|
@@ -217,3 +238,139 @@ export default (new Transformer({
|
|
|
217
238
|
};
|
|
218
239
|
},
|
|
219
240
|
}): Transformer);
|
|
241
|
+
|
|
242
|
+
async function compileCSSModules(asset, env, program, resolve, options) {
|
|
243
|
+
let cssModules;
|
|
244
|
+
|
|
245
|
+
let code = asset.isASTDirty() ? null : await asset.getCode();
|
|
246
|
+
if (code == null || COMPOSES_RE.test(code)) {
|
|
247
|
+
program.walkDecls(decl => {
|
|
248
|
+
let [, importPath] = FROM_IMPORT_RE.exec(decl.value) || [];
|
|
249
|
+
if (decl.prop === 'composes' && importPath != null) {
|
|
250
|
+
let parsed = valueParser(decl.value);
|
|
251
|
+
let start = (decl.source.start: any);
|
|
252
|
+
|
|
253
|
+
parsed.walk(node => {
|
|
254
|
+
if (node.type === 'string') {
|
|
255
|
+
asset.addDependency({
|
|
256
|
+
specifier: importPath,
|
|
257
|
+
specifierType: 'url',
|
|
258
|
+
loc: start
|
|
259
|
+
? {
|
|
260
|
+
filePath: asset.filePath,
|
|
261
|
+
start,
|
|
262
|
+
end: {
|
|
263
|
+
line: start.line,
|
|
264
|
+
column: start.column + importPath.length,
|
|
265
|
+
},
|
|
266
|
+
}
|
|
267
|
+
: undefined,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
let postcssModules = await options.packageManager.require(
|
|
276
|
+
'postcss-modules',
|
|
277
|
+
asset.filePath,
|
|
278
|
+
{
|
|
279
|
+
range: '^4.3.0',
|
|
280
|
+
saveDev: true,
|
|
281
|
+
shouldAutoInstall: options.shouldAutoInstall,
|
|
282
|
+
},
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
let {root} = await postcss([
|
|
286
|
+
postcssModules({
|
|
287
|
+
getJSON: (filename, json) => (cssModules = json),
|
|
288
|
+
Loader: await createLoader(asset, resolve, options),
|
|
289
|
+
generateScopedName: (name, filename) =>
|
|
290
|
+
`${name}_${hashString(
|
|
291
|
+
path.relative(options.projectRoot, filename),
|
|
292
|
+
).substr(0, 6)}`,
|
|
293
|
+
}),
|
|
294
|
+
]).process(program, {from: asset.filePath, to: asset.filePath});
|
|
295
|
+
asset.setAST({
|
|
296
|
+
type: 'postcss',
|
|
297
|
+
version: '8.2.1',
|
|
298
|
+
program: root.toJSON(),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
let assets = [asset];
|
|
302
|
+
if (cssModules) {
|
|
303
|
+
// $FlowFixMe
|
|
304
|
+
let cssModulesList = (Object.entries(cssModules): Array<[string, string]>);
|
|
305
|
+
let deps = asset.getDependencies().filter(dep => dep.priority === 'sync');
|
|
306
|
+
let code: string;
|
|
307
|
+
if (deps.length > 0) {
|
|
308
|
+
code = `
|
|
309
|
+
module.exports = Object.assign({}, ${deps
|
|
310
|
+
.map(dep => `require(${JSON.stringify(dep.specifier)})`)
|
|
311
|
+
.join(', ')}, ${JSON.stringify(cssModules, null, 2)});
|
|
312
|
+
`;
|
|
313
|
+
} else {
|
|
314
|
+
code = cssModulesList
|
|
315
|
+
.map(
|
|
316
|
+
// This syntax enables shaking the invidual statements, so that unused classes don't even exist in JS.
|
|
317
|
+
([className, classNameHashed]) =>
|
|
318
|
+
`module.exports[${JSON.stringify(className)}] = ${JSON.stringify(
|
|
319
|
+
classNameHashed,
|
|
320
|
+
)};`,
|
|
321
|
+
)
|
|
322
|
+
.join('\n');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
asset.symbols.ensure();
|
|
326
|
+
for (let [k, v] of cssModulesList) {
|
|
327
|
+
asset.symbols.set(k, v);
|
|
328
|
+
}
|
|
329
|
+
asset.symbols.set('default', 'default');
|
|
330
|
+
|
|
331
|
+
assets.push({
|
|
332
|
+
type: 'js',
|
|
333
|
+
content: code,
|
|
334
|
+
env,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return assets;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function createLoader(
|
|
341
|
+
asset: MutableAsset,
|
|
342
|
+
resolve: (from: FilePath, to: string) => Promise<FilePath>,
|
|
343
|
+
options: PluginOptions,
|
|
344
|
+
) {
|
|
345
|
+
let {default: FileSystemLoader} = await options.packageManager.require(
|
|
346
|
+
'postcss-modules/build/css-loader-core/loader',
|
|
347
|
+
asset.filePath,
|
|
348
|
+
);
|
|
349
|
+
return class ParcelFileSystemLoader extends FileSystemLoader {
|
|
350
|
+
async fetch(composesPath, relativeTo) {
|
|
351
|
+
let importPath = composesPath.replace(/^["']|["']$/g, '');
|
|
352
|
+
let resolved = await resolve(relativeTo, importPath);
|
|
353
|
+
let rootRelativePath = path.resolve(path.dirname(relativeTo), resolved);
|
|
354
|
+
let root = path.resolve('/');
|
|
355
|
+
// fixes an issue on windows which is part of the css-modules-loader-core
|
|
356
|
+
// see https://github.com/css-modules/css-modules-loader-core/issues/230
|
|
357
|
+
if (rootRelativePath.startsWith(root)) {
|
|
358
|
+
rootRelativePath = rootRelativePath.substr(root.length);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let source = await asset.fs.readFile(resolved, 'utf-8');
|
|
362
|
+
let {exportTokens} = await this.core.load(
|
|
363
|
+
source,
|
|
364
|
+
rootRelativePath,
|
|
365
|
+
undefined,
|
|
366
|
+
// $FlowFixMe[method-unbinding]
|
|
367
|
+
this.fetch.bind(this),
|
|
368
|
+
);
|
|
369
|
+
return exportTokens;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
get finalSource() {
|
|
373
|
+
return '';
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
}
|