happy-css-modules 3.0.0 → 3.0.1

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.
@@ -4,14 +4,7 @@ import type { Resolver } from '../resolver/index.js';
4
4
  import { createDefaultResolver } from '../resolver/index.js';
5
5
  import { createDefaultTransformer, type Transformer } from '../transformer/index.js';
6
6
  import { unique, uniqueBy } from '../util.js';
7
- import {
8
- getOriginalLocation,
9
- generateLocalTokenNames,
10
- parseAtImport,
11
- type Location,
12
- parseComposesDeclarationWithFromUrl,
13
- collectNodes,
14
- } from './postcss.js';
7
+ import { getOriginalLocation, generateLocalTokenNames, parseAtImport, type Location, collectNodes } from './postcss.js';
15
8
 
16
9
  export { collectNodes, type Location } from './postcss.js';
17
10
 
@@ -38,7 +31,7 @@ type CacheEntry = {
38
31
 
39
32
  /** The result of `Locator#load`. */
40
33
  export type LoadResult = {
41
- /** The path of the file imported from the source file with `@import` or `composes`. */
34
+ /** The path of the file imported from the source file with `@import`. */
42
35
  dependencies: string[];
43
36
  /** The tokens exported by the source file. */
44
37
  tokens: Token[];
@@ -165,7 +158,7 @@ export class Locator {
165
158
 
166
159
  const tokens: Token[] = [];
167
160
 
168
- const { atImports, classSelectors, composesDeclarations } = collectNodes(ast);
161
+ const { atImports, classSelectors } = collectNodes(ast);
169
162
 
170
163
  // Load imported sheets recursively.
171
164
  for (const atImport of atImports) {
@@ -195,20 +188,6 @@ export class Locator {
195
188
  });
196
189
  }
197
190
 
198
- // Load imported tokens by the names recursively.
199
- for (const composesDeclaration of composesDeclarations) {
200
- const declarationDetail = parseComposesDeclarationWithFromUrl(composesDeclaration);
201
- if (!declarationDetail) continue;
202
- if (isIgnoredSpecifier(declarationDetail.from)) continue;
203
- // eslint-disable-next-line no-await-in-loop
204
- const from = await this.resolver(declarationDetail.from, { request: filePath });
205
- // eslint-disable-next-line no-await-in-loop
206
- const result = await this._load(from);
207
- const externalTokens = result.tokens.filter((token) => declarationDetail.tokenNames.includes(token.name));
208
- dependencies.push(from, ...result.dependencies);
209
- tokens.push(...externalTokens);
210
- }
211
-
212
191
  const result: LoadResult = {
213
192
  dependencies: unique(dependencies).filter((dep) => dep !== filePath),
214
193
  tokens: normalizeTokens(tokens),
@@ -1,18 +1,6 @@
1
1
  import dedent from 'dedent';
2
- import {
3
- createRoot,
4
- createClassSelectors,
5
- createAtImports,
6
- createComposesDeclarations,
7
- createFixtures,
8
- } from '../test-util/util.js';
9
- import {
10
- generateLocalTokenNames,
11
- getOriginalLocation,
12
- parseAtImport,
13
- parseComposesDeclarationWithFromUrl,
14
- collectNodes,
15
- } from './postcss.js';
2
+ import { createRoot, createClassSelectors, createAtImports, createFixtures } from '../test-util/util.js';
3
+ import { generateLocalTokenNames, getOriginalLocation, parseAtImport, collectNodes } from './postcss.js';
16
4
 
17
5
  describe('generateLocalTokenNames', () => {
18
6
  test('basic', async () => {
@@ -39,10 +27,6 @@ describe('generateLocalTokenNames', () => {
39
27
  .local_class_name_3 {}
40
28
  }
41
29
  :local(.local_class_name_4) {}
42
- .composes_target {}
43
- .composes {
44
- composes: composes_target;
45
- }
46
30
  `),
47
31
  ),
48
32
  ).toStrictEqual([
@@ -62,8 +46,6 @@ describe('generateLocalTokenNames', () => {
62
46
  'local_class_name_2',
63
47
  'local_class_name_3',
64
48
  'local_class_name_4',
65
- 'composes_target',
66
- 'composes',
67
49
  ]);
68
50
  });
69
51
  test('does not track styles imported by @import in other file because it is not a local token', async () => {
@@ -236,23 +218,6 @@ describe('getOriginalLocation', () => {
236
218
  `{ filePath: "/test/test.css", start: { line: 6, column: 8 }, end: { line: 6, column: 26 } }`,
237
219
  );
238
220
  });
239
- test('composes', () => {
240
- // eslint-disable-next-line @typescript-eslint/naming-convention
241
- const [composes_target, composes] = createClassSelectors(
242
- createRoot(dedent`
243
- .composes_target {}
244
- .composes {
245
- composes: composes_target;
246
- }
247
- `),
248
- );
249
- expect(getOriginalLocation(composes_target!.rule, composes_target!.classSelector)).toMatchInlineSnapshot(
250
- `{ filePath: "/test/test.css", start: { line: 1, column: 1 }, end: { line: 1, column: 16 } }`,
251
- );
252
- expect(getOriginalLocation(composes!.rule, composes!.classSelector)).toMatchInlineSnapshot(
253
- `{ filePath: "/test/test.css", start: { line: 2, column: 1 }, end: { line: 2, column: 9 } }`,
254
- );
255
- });
256
221
  test('with_newline', () => {
257
222
  // eslint-disable-next-line @typescript-eslint/naming-convention
258
223
  const [with_newline_1, with_newline_2, with_newline_3] = createClassSelectors(
@@ -280,23 +245,20 @@ test('collectNodes', () => {
280
245
  @import;
281
246
  @import "test.css";
282
247
  @ignored;
283
- .a { ignored: "ignored"; composes: a; }
284
- .b { ignored: "ignored"; composes: b; }
248
+ .a { ignored: "ignored"; }
249
+ .b { ignored: "ignored"; }
285
250
  `);
286
251
 
287
- const { atImports, classSelectors, composesDeclarations } = collectNodes(ast);
252
+ const { atImports, classSelectors } = collectNodes(ast);
288
253
 
289
254
  expect(atImports).toHaveLength(2);
290
255
  expect(atImports[0]!.toString()).toEqual('@import');
291
256
  expect(atImports[1]!.toString()).toEqual('@import "test.css"');
292
257
  expect(classSelectors).toHaveLength(2);
293
- expect(classSelectors[0]!.rule.toString()).toEqual('.a { ignored: "ignored"; composes: a; }');
258
+ expect(classSelectors[0]!.rule.toString()).toEqual('.a { ignored: "ignored"; }');
294
259
  expect(classSelectors[0]!.classSelector.toString()).toEqual('.a');
295
- expect(classSelectors[1]!.rule.toString()).toEqual('.b { ignored: "ignored"; composes: b; }');
260
+ expect(classSelectors[1]!.rule.toString()).toEqual('.b { ignored: "ignored"; }');
296
261
  expect(classSelectors[1]!.classSelector.toString()).toEqual('.b');
297
- expect(composesDeclarations).toHaveLength(2);
298
- expect(composesDeclarations[0]!.toString()).toEqual('composes: a');
299
- expect(composesDeclarations[1]!.toString()).toEqual('composes: b');
300
262
  });
301
263
 
302
264
  test('parseAtImport', () => {
@@ -315,32 +277,3 @@ test('parseAtImport', () => {
315
277
  expect(parseAtImport(atImports[3]!)).toBe('test.css');
316
278
  expect(parseAtImport(atImports[4]!)).toBe('test.css');
317
279
  });
318
-
319
- test('parseComposesDeclarationWithFromUrl', () => {
320
- const composesDeclarations = createComposesDeclarations(
321
- createRoot(dedent`
322
- .a {
323
- composes: a;
324
- composes: a b c;
325
- composes: a from "test.css";
326
- composes: a b c from "test.css";
327
- composes: from from from from "test.css";
328
- /* NOTE: CSS Modules do not support '... from url("test.css")'. */
329
- }
330
- `),
331
- );
332
- expect(parseComposesDeclarationWithFromUrl(composesDeclarations[0]!)).toStrictEqual(undefined);
333
- expect(parseComposesDeclarationWithFromUrl(composesDeclarations[1]!)).toStrictEqual(undefined);
334
- expect(parseComposesDeclarationWithFromUrl(composesDeclarations[2]!)).toStrictEqual({
335
- from: 'test.css',
336
- tokenNames: ['a'],
337
- });
338
- expect(parseComposesDeclarationWithFromUrl(composesDeclarations[3]!)).toStrictEqual({
339
- from: 'test.css',
340
- tokenNames: ['a', 'b', 'c'],
341
- });
342
- expect(parseComposesDeclarationWithFromUrl(composesDeclarations[4]!)).toStrictEqual({
343
- from: 'test.css',
344
- tokenNames: ['from', 'from', 'from'], // do not deduplicate.
345
- });
346
- });
@@ -54,9 +54,9 @@ export async function generateLocalTokenNames(ast: Root): Promise<string[]> {
54
54
  postcss
55
55
  .default()
56
56
  // postcss-modules collects tokens (i.e., includes external tokens) by following
57
- // the dependencies specified in the @import and composes properties.
57
+ // the dependencies specified in the @import.
58
58
  // However, we do not want `generateLocalTokenNames` to return external tokens.
59
- // So we remove the @import and composes properties beforehand.
59
+ // So we remove the @import beforehand.
60
60
  .use(removeDependenciesPlugin())
61
61
  .use(
62
62
  modules({
@@ -154,7 +154,6 @@ function isComposesDeclaration(node: Node): node is Declaration {
154
154
  type CollectNodesResult = {
155
155
  atImports: AtRule[];
156
156
  classSelectors: { rule: Rule; classSelector: ClassName }[];
157
- composesDeclarations: Declaration[];
158
157
  };
159
158
 
160
159
  /**
@@ -164,7 +163,6 @@ type CollectNodesResult = {
164
163
  export function collectNodes(ast: Root): CollectNodesResult {
165
164
  const atImports: AtRule[] = [];
166
165
  const classSelectors: { rule: Rule; classSelector: ClassName }[] = [];
167
- const composesDeclarations: Declaration[] = [];
168
166
  ast.walk((node) => {
169
167
  if (isAtImportNode(node)) {
170
168
  atImports.push(node);
@@ -183,11 +181,9 @@ export function collectNodes(ast: Root): CollectNodesResult {
183
181
  }
184
182
  });
185
183
  }).processSync(node);
186
- } else if (isComposesDeclaration(node)) {
187
- composesDeclarations.push(node);
188
184
  }
189
185
  });
190
- return { atImports, classSelectors, composesDeclarations };
186
+ return { atImports, classSelectors };
191
187
  }
192
188
 
193
189
  /**
@@ -206,40 +202,3 @@ export function parseAtImport(atImport: AtRule): string | undefined {
206
202
  }
207
203
  return undefined;
208
204
  }
209
-
210
- /**
211
- * Parse `composes` declaration with `from <url>`.
212
- * If the declaration is not found or do not have `from <url>`, return `undefined`.
213
- * @param composesDeclaration The `composes` declaration to parse.
214
- * @returns The information of the declaration.
215
- */
216
- export function parseComposesDeclarationWithFromUrl(
217
- composesDeclaration: Declaration,
218
- ): { from: string; tokenNames: string[] } | undefined {
219
- // NOTE: `composes` property syntax is...
220
- // - syntax: `composes: <class-name> [...<class-name>] [from <url>];`
221
- // - variables:
222
- // - `<class-name>`: `<sting>`
223
- // - `<url>`: `<string>`
224
- // - ref:
225
- // - https://github.com/css-modules/css-modules#composition
226
- // - https://github.com/css-modules/css-modules#composing-from-other-files
227
- // - https://github.com/css-modules/postcss-modules-extract-imports#specification
228
-
229
- const nodes = valueParser(composesDeclaration.value).nodes;
230
- if (nodes.length < 5) return undefined;
231
-
232
- const classNamesOrSpaces = nodes.slice(0, -3);
233
- const [from, , url] = nodes.slice(-3);
234
-
235
- const classNames = classNamesOrSpaces.filter((node) => node.type === 'word');
236
-
237
- // validate nodes
238
- if (from === undefined) return undefined;
239
- if (from.type !== 'word' || from.value !== 'from') return undefined;
240
- if (url === undefined) return undefined;
241
- if (url.type !== 'string') return undefined;
242
- if (classNames.length === 0) return undefined;
243
-
244
- return { from: url.value, tokenNames: classNames.map((node) => node.value) };
245
- }
@@ -2,7 +2,7 @@ import { constants, mkdirSync, realpathSync, rmSync, writeFileSync } from 'fs';
2
2
  import { access } from 'fs/promises';
3
3
  import { tmpdir } from 'os';
4
4
  import { dirname, join, resolve } from 'path';
5
- import postcss, { type Root, type Rule, type AtRule, type Declaration } from 'postcss';
5
+ import postcss, { type Root, type Rule, type AtRule } from 'postcss';
6
6
  import { type ClassName } from 'postcss-selector-parser';
7
7
  import { type Token, collectNodes, type Location } from '../locator/index.js';
8
8
  import { sleepSync } from '../util.js';
@@ -25,10 +25,6 @@ export function createClassSelectors(root: Root): { rule: Rule; classSelector: C
25
25
  return collectNodes(root).classSelectors;
26
26
  }
27
27
 
28
- export function createComposesDeclarations(root: Root): Declaration[] {
29
- return collectNodes(root).composesDeclarations;
30
- }
31
-
32
28
  export function fakeToken(args: {
33
29
  name: Token['name'];
34
30
  originalLocations: { filePath?: Location['filePath']; start?: Location['start'] }[];
@@ -23,8 +23,6 @@ test('handles less features', async () => {
23
23
  &_2 { dummy: ''; }
24
24
  .b_1();
25
25
  .b_2();
26
- composes: a_1; // css module feature test (composes)
27
- composes: c from './3.less'; // css module feature test (composes from other file)
28
26
  }
29
27
  `,
30
28
  '/test/2.less': dedent`
@@ -40,7 +38,7 @@ test('handles less features', async () => {
40
38
  // FIXME: The end position of 'a_2_2' is incorrect.
41
39
  expect(result).toMatchInlineSnapshot(`
42
40
  {
43
- dependencies: ["<fixtures>/test/2.less", "<fixtures>/test/3.less"],
41
+ dependencies: ["<fixtures>/test/2.less"],
44
42
  tokens: [
45
43
  {
46
44
  name: "b_1",
@@ -72,12 +70,6 @@ test('handles less features', async () => {
72
70
  { filePath: "<fixtures>/test/1.less", start: { line: 7, column: 3 }, end: { line: 7, column: 8 } },
73
71
  ],
74
72
  },
75
- {
76
- name: "c",
77
- originalLocations: [
78
- { filePath: "<fixtures>/test/3.less", start: { line: 1, column: 1 }, end: { line: 1, column: 2 } },
79
- ],
80
- },
81
73
  ],
82
74
  }
83
75
  `);
@@ -22,8 +22,6 @@ test('handles sass features', async () => {
22
22
  // sass feature test (nesting)
23
23
  .a_2_1 { dummy: ''; }
24
24
  &_2 { dummy: ''; }
25
- composes: a_1; // css module feature test (composes)
26
- composes: d from './4.scss'; // css module feature test (composes from other file)
27
25
  }
28
26
  `,
29
27
  '/test/2.scss': dedent`
@@ -46,7 +44,7 @@ test('handles sass features', async () => {
46
44
  // FIXME: The end position of 'a_2_2' is incorrect.
47
45
  expect(result).toMatchInlineSnapshot(`
48
46
  {
49
- dependencies: ["<fixtures>/test/2.scss", "<fixtures>/test/3.scss", "<fixtures>/test/4.scss"],
47
+ dependencies: ["<fixtures>/test/2.scss", "<fixtures>/test/3.scss"],
50
48
  tokens: [
51
49
  {
52
50
  name: "b_1",
@@ -85,12 +83,6 @@ test('handles sass features', async () => {
85
83
  { filePath: "<fixtures>/test/1.scss", start: { line: 8, column: 3 }, end: { line: 8, column: 8 } },
86
84
  ],
87
85
  },
88
- {
89
- name: "d",
90
- originalLocations: [
91
- { filePath: "<fixtures>/test/4.scss", start: { line: 1, column: 1 }, end: { line: 1, column: 2 } },
92
- ],
93
- },
94
86
  ],
95
87
  }
96
88
  `);
package/src/util.ts CHANGED
@@ -66,7 +66,7 @@ export async function exists(path: string): Promise<boolean> {
66
66
  }
67
67
 
68
68
  export function isMatchByGlob(filePath: string, pattern: string, options: { cwd: string }): boolean {
69
- return minimatch(filePath, join(options.cwd, pattern));
69
+ return minimatch(filePath, join(options.cwd, pattern), { windowsPathsNoEscape: true });
70
70
  }
71
71
 
72
72
  export function getPackageJson() {