@parcel/transformer-css 2.0.0-nightly.97 → 2.0.0-nightly.982

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