happy-css-modules 3.0.0 → 3.1.0
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/package.json +1 -1
- package/src/emitter/dts.test.ts +3 -3
- package/src/emitter/dts.ts +59 -37
- package/src/emitter/index.test.ts +3 -3
- package/src/integration-test/go-to-definition.test.ts +0 -35
- package/src/locator/index.test.ts +152 -158
- package/src/locator/index.ts +44 -37
- package/src/locator/postcss.test.ts +118 -76
- package/src/locator/postcss.ts +116 -73
- package/src/test-util/util.ts +24 -23
- package/src/transformer/less-transformer.test.ts +26 -24
- package/src/transformer/postcss-transformer.test.ts +5 -3
- package/src/transformer/scss-transformer.test.ts +39 -28
- package/src/util.ts +1 -1
- package/dist/cli.d.ts +0 -6
- package/dist/cli.js +0 -121
- package/dist/cli.js.map +0 -1
- package/dist/cli.test.d.ts +0 -1
- package/dist/cli.test.js +0 -66
- package/dist/cli.test.js.map +0 -1
- package/dist/config.d.ts +0 -1
- package/dist/config.js +0 -2
- package/dist/config.js.map +0 -1
- package/dist/emitter/dts.d.ts +0 -14
- package/dist/emitter/dts.js +0 -111
- package/dist/emitter/dts.js.map +0 -1
- package/dist/emitter/dts.test.d.ts +0 -1
- package/dist/emitter/dts.test.js +0 -205
- package/dist/emitter/dts.test.js.map +0 -1
- package/dist/emitter/file-system.d.ts +0 -6
- package/dist/emitter/file-system.js +0 -26
- package/dist/emitter/file-system.js.map +0 -1
- package/dist/emitter/file-system.test.d.ts +0 -1
- package/dist/emitter/file-system.test.js +0 -34
- package/dist/emitter/file-system.test.js.map +0 -1
- package/dist/emitter/index.d.ts +0 -34
- package/dist/emitter/index.js +0 -49
- package/dist/emitter/index.js.map +0 -1
- package/dist/emitter/index.test.d.ts +0 -1
- package/dist/emitter/index.test.js +0 -68
- package/dist/emitter/index.test.js.map +0 -1
- package/dist/emitter/source-map.d.ts +0 -8
- package/dist/emitter/source-map.js +0 -16
- package/dist/emitter/source-map.js.map +0 -1
- package/dist/emitter/source-map.test.d.ts +0 -1
- package/dist/emitter/source-map.test.js +0 -13
- package/dist/emitter/source-map.test.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -6
- package/dist/index.js.map +0 -1
- package/dist/integration-test/go-to-definition.test.d.ts +0 -1
- package/dist/integration-test/go-to-definition.test.js +0 -369
- package/dist/integration-test/go-to-definition.test.js.map +0 -1
- package/dist/library/source-map/index.d.ts +0 -8
- package/dist/library/source-map/index.js +0 -5
- package/dist/library/source-map/index.js.map +0 -1
- package/dist/locator/index.d.ts +0 -44
- package/dist/locator/index.js +0 -162
- package/dist/locator/index.js.map +0 -1
- package/dist/locator/index.test.d.ts +0 -1
- package/dist/locator/index.test.js +0 -395
- package/dist/locator/index.test.js.map +0 -1
- package/dist/locator/postcss.d.ts +0 -64
- package/dist/locator/postcss.js +0 -208
- package/dist/locator/postcss.js.map +0 -1
- package/dist/locator/postcss.test.d.ts +0 -1
- package/dist/locator/postcss.test.js +0 -242
- package/dist/locator/postcss.test.js.map +0 -1
- package/dist/logger.d.ts +0 -9
- package/dist/logger.js +0 -28
- package/dist/logger.js.map +0 -1
- package/dist/regression-test/issue-168.test.d.ts +0 -1
- package/dist/regression-test/issue-168.test.js +0 -30
- package/dist/regression-test/issue-168.test.js.map +0 -1
- package/dist/resolver/index.d.ts +0 -22
- package/dist/resolver/index.js +0 -44
- package/dist/resolver/index.js.map +0 -1
- package/dist/resolver/index.test.d.ts +0 -1
- package/dist/resolver/index.test.js +0 -16
- package/dist/resolver/index.test.js.map +0 -1
- package/dist/resolver/node-resolver.d.ts +0 -2
- package/dist/resolver/node-resolver.js +0 -6
- package/dist/resolver/node-resolver.js.map +0 -1
- package/dist/resolver/node-resolver.test.d.ts +0 -1
- package/dist/resolver/node-resolver.test.js +0 -25
- package/dist/resolver/node-resolver.test.js.map +0 -1
- package/dist/resolver/relative-resolver.d.ts +0 -2
- package/dist/resolver/relative-resolver.js +0 -5
- package/dist/resolver/relative-resolver.js.map +0 -1
- package/dist/resolver/relative-resolver.test.d.ts +0 -1
- package/dist/resolver/relative-resolver.test.js +0 -12
- package/dist/resolver/relative-resolver.test.js.map +0 -1
- package/dist/resolver/webpack-resolver.d.ts +0 -24
- package/dist/resolver/webpack-resolver.js +0 -91
- package/dist/resolver/webpack-resolver.js.map +0 -1
- package/dist/resolver/webpack-resolver.test.d.ts +0 -1
- package/dist/resolver/webpack-resolver.test.js +0 -89
- package/dist/resolver/webpack-resolver.test.js.map +0 -1
- package/dist/runner.d.ts +0 -76
- package/dist/runner.js +0 -120
- package/dist/runner.js.map +0 -1
- package/dist/runner.test.d.ts +0 -1
- package/dist/runner.test.js +0 -287
- package/dist/runner.test.js.map +0 -1
- package/dist/test-util/jest/resolver.cjs +0 -31
- package/dist/test-util/jest/resolver.cjs.map +0 -1
- package/dist/test-util/jest/resolver.d.cts +0 -16
- package/dist/test-util/tsserver.d.ts +0 -31
- package/dist/test-util/tsserver.js +0 -112
- package/dist/test-util/tsserver.js.map +0 -1
- package/dist/test-util/util.d.ts +0 -29
- package/dist/test-util/util.js +0 -84
- package/dist/test-util/util.js.map +0 -1
- package/dist/transformer/index.d.ts +0 -30
- package/dist/transformer/index.js +0 -25
- package/dist/transformer/index.js.map +0 -1
- package/dist/transformer/index.test.d.ts +0 -1
- package/dist/transformer/index.test.js +0 -66
- package/dist/transformer/index.test.js.map +0 -1
- package/dist/transformer/less-transformer.d.ts +0 -2
- package/dist/transformer/less-transformer.js +0 -45
- package/dist/transformer/less-transformer.js.map +0 -1
- package/dist/transformer/less-transformer.test.d.ts +0 -1
- package/dist/transformer/less-transformer.test.js +0 -132
- package/dist/transformer/less-transformer.test.js.map +0 -1
- package/dist/transformer/postcss-transformer.d.ts +0 -12
- package/dist/transformer/postcss-transformer.js +0 -32
- package/dist/transformer/postcss-transformer.js.map +0 -1
- package/dist/transformer/postcss-transformer.test.d.ts +0 -1
- package/dist/transformer/postcss-transformer.test.js +0 -176
- package/dist/transformer/postcss-transformer.test.js.map +0 -1
- package/dist/transformer/scss-transformer.d.ts +0 -2
- package/dist/transformer/scss-transformer.js +0 -40
- package/dist/transformer/scss-transformer.js.map +0 -1
- package/dist/transformer/scss-transformer.test.d.ts +0 -1
- package/dist/transformer/scss-transformer.test.js +0 -138
- package/dist/transformer/scss-transformer.test.js.map +0 -1
- package/dist/util.d.ts +0 -21
- package/dist/util.js +0 -72
- package/dist/util.js.map +0 -1
- package/dist/util.test.d.ts +0 -1
- package/dist/util.test.js +0 -74
- package/dist/util.test.js.map +0 -1
|
@@ -1,29 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { jest } from '@jest/globals';
|
|
4
4
|
import dedent from 'dedent';
|
|
5
|
-
import { createDefaultTransformer } from '../index.js';
|
|
6
|
-
import { createFixtures,
|
|
5
|
+
import { Locator, createDefaultTransformer } from '../index.js';
|
|
6
|
+
import { createFixtures, getFixturePath } from '../test-util/util.js';
|
|
7
7
|
import { sleepSync } from '../util.js';
|
|
8
8
|
|
|
9
|
-
const readFileSpy = jest.spyOn(fs, 'readFile');
|
|
10
|
-
// In ESM, for some reason, we need to explicitly mock module
|
|
11
|
-
jest.unstable_mockModule('fs/promises', () => ({
|
|
12
|
-
...fs, // Inherit native functions (e.g., fs.stat)
|
|
13
|
-
readFile: readFileSpy,
|
|
14
|
-
}));
|
|
15
|
-
|
|
16
|
-
// After the mock of fs/promises is complete, . /index.js after the mock of fs/promises is complete.
|
|
17
|
-
// ref: https://www.coolcomputerclub.com/posts/jest-hoist-await/
|
|
18
|
-
const { Locator } = await import('./index.js');
|
|
19
|
-
// NOTE: ../test/util.js depends on . /index.js, so it must also be imported dynamically...
|
|
20
|
-
|
|
21
9
|
const locator = new Locator();
|
|
22
10
|
|
|
23
|
-
afterEach(() => {
|
|
24
|
-
readFileSpy.mockClear();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
11
|
test('basic', async () => {
|
|
28
12
|
createFixtures({
|
|
29
13
|
'/test/1.css': dedent`
|
|
@@ -38,15 +22,19 @@ test('basic', async () => {
|
|
|
38
22
|
tokens: [
|
|
39
23
|
{
|
|
40
24
|
name: "a",
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
25
|
+
originalLocation: {
|
|
26
|
+
filePath: "<fixtures>/test/1.css",
|
|
27
|
+
start: { line: 1, column: 1 },
|
|
28
|
+
end: { line: 1, column: 2 },
|
|
29
|
+
},
|
|
44
30
|
},
|
|
45
31
|
{
|
|
46
32
|
name: "b",
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
33
|
+
originalLocation: {
|
|
34
|
+
filePath: "<fixtures>/test/1.css",
|
|
35
|
+
start: { line: 2, column: 1 },
|
|
36
|
+
end: { line: 2, column: 2 },
|
|
37
|
+
},
|
|
50
38
|
},
|
|
51
39
|
],
|
|
52
40
|
}
|
|
@@ -90,51 +78,86 @@ test('tracks other files when `@import` is present', async () => {
|
|
|
90
78
|
tokens: [
|
|
91
79
|
{
|
|
92
80
|
name: "a",
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
originalLocation: {
|
|
82
|
+
filePath: "<fixtures>/test/2.css",
|
|
83
|
+
start: { line: 1, column: 1 },
|
|
84
|
+
end: { line: 1, column: 2 },
|
|
85
|
+
},
|
|
96
86
|
},
|
|
97
87
|
{
|
|
98
88
|
name: "b",
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
89
|
+
originalLocation: {
|
|
90
|
+
filePath: "<fixtures>/test/3.css",
|
|
91
|
+
start: { line: 1, column: 1 },
|
|
92
|
+
end: { line: 1, column: 2 },
|
|
93
|
+
},
|
|
102
94
|
},
|
|
103
95
|
{
|
|
104
96
|
name: "c",
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
97
|
+
originalLocation: {
|
|
98
|
+
filePath: "<fixtures>/test/4.css",
|
|
99
|
+
start: { line: 1, column: 1 },
|
|
100
|
+
end: { line: 1, column: 2 },
|
|
101
|
+
},
|
|
108
102
|
},
|
|
109
103
|
{
|
|
110
104
|
name: "d",
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
105
|
+
originalLocation: {
|
|
106
|
+
filePath: "<fixtures>/test/5-recursive.css",
|
|
107
|
+
start: { line: 1, column: 1 },
|
|
108
|
+
end: { line: 1, column: 2 },
|
|
109
|
+
},
|
|
114
110
|
},
|
|
115
111
|
],
|
|
116
112
|
}
|
|
117
113
|
`);
|
|
118
114
|
});
|
|
119
115
|
|
|
120
|
-
test('
|
|
116
|
+
test('does not track other files by `composes`', async () => {
|
|
121
117
|
createFixtures({
|
|
122
118
|
'/test/1.css': dedent`
|
|
123
119
|
.a {
|
|
124
120
|
composes: b from './2.css';
|
|
125
|
-
composes: c
|
|
126
|
-
composes: e from '${getFixturePath('/test/4.css')}';
|
|
121
|
+
composes: c from './3.css'; /* non-existent file */
|
|
127
122
|
}
|
|
128
123
|
`,
|
|
129
124
|
'/test/2.css': dedent`
|
|
130
125
|
.b {}
|
|
131
126
|
`,
|
|
127
|
+
});
|
|
128
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
129
|
+
expect(result).toMatchInlineSnapshot(`
|
|
130
|
+
{
|
|
131
|
+
dependencies: [],
|
|
132
|
+
tokens: [
|
|
133
|
+
{
|
|
134
|
+
name: "a",
|
|
135
|
+
originalLocation: {
|
|
136
|
+
filePath: "<fixtures>/test/1.css",
|
|
137
|
+
start: { line: 1, column: 1 },
|
|
138
|
+
end: { line: 1, column: 2 },
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
}
|
|
143
|
+
`);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('tracks other files when `@value` is present', async () => {
|
|
147
|
+
createFixtures({
|
|
148
|
+
'/test/1.css': dedent`
|
|
149
|
+
@value a from './2.css';
|
|
150
|
+
@value b from '3.css';
|
|
151
|
+
@value c from '${getFixturePath('/test/4.css')}';
|
|
152
|
+
`,
|
|
153
|
+
'/test/2.css': dedent`
|
|
154
|
+
@value a: 1;
|
|
155
|
+
`,
|
|
132
156
|
'/test/3.css': dedent`
|
|
133
|
-
|
|
134
|
-
.d {}
|
|
157
|
+
@value b: 2;
|
|
135
158
|
`,
|
|
136
159
|
'/test/4.css': dedent`
|
|
137
|
-
|
|
160
|
+
@value c: 3;
|
|
138
161
|
`,
|
|
139
162
|
});
|
|
140
163
|
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
@@ -144,103 +167,94 @@ test('tracks other files when `composes` is present', async () => {
|
|
|
144
167
|
tokens: [
|
|
145
168
|
{
|
|
146
169
|
name: "a",
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
170
|
+
originalLocation: {
|
|
171
|
+
filePath: "<fixtures>/test/2.css",
|
|
172
|
+
start: { line: 1, column: 8 },
|
|
173
|
+
end: { line: 1, column: 9 },
|
|
174
|
+
},
|
|
150
175
|
},
|
|
151
176
|
{
|
|
152
177
|
name: "b",
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
178
|
+
originalLocation: {
|
|
179
|
+
filePath: "<fixtures>/test/3.css",
|
|
180
|
+
start: { line: 1, column: 8 },
|
|
181
|
+
end: { line: 1, column: 9 },
|
|
182
|
+
},
|
|
156
183
|
},
|
|
157
184
|
{
|
|
158
185
|
name: "c",
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
name: "d",
|
|
165
|
-
originalLocations: [
|
|
166
|
-
{ filePath: "<fixtures>/test/3.css", start: { line: 2, column: 1 }, end: { line: 2, column: 2 } },
|
|
167
|
-
],
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
name: "e",
|
|
171
|
-
originalLocations: [
|
|
172
|
-
{ filePath: "<fixtures>/test/4.css", start: { line: 1, column: 1 }, end: { line: 1, column: 2 } },
|
|
173
|
-
],
|
|
186
|
+
originalLocation: {
|
|
187
|
+
filePath: "<fixtures>/test/4.css",
|
|
188
|
+
start: { line: 1, column: 8 },
|
|
189
|
+
end: { line: 1, column: 9 },
|
|
190
|
+
},
|
|
174
191
|
},
|
|
175
192
|
],
|
|
176
193
|
}
|
|
177
194
|
`);
|
|
178
195
|
});
|
|
179
196
|
|
|
180
|
-
test('
|
|
197
|
+
test('unique tokens', async () => {
|
|
181
198
|
createFixtures({
|
|
182
199
|
'/test/1.css': dedent`
|
|
183
200
|
/* duplicate import */
|
|
184
201
|
@import './2.css';
|
|
185
202
|
@import '2.css';
|
|
186
|
-
.a {
|
|
187
|
-
/* duplicate composes */
|
|
188
|
-
composes: c from './3.css';
|
|
189
|
-
composes: c from '3.css';
|
|
190
|
-
composes: c c from './3.css';
|
|
191
|
-
/* duplicate import and composes */
|
|
192
|
-
composes: b from './2.css';
|
|
193
|
-
}
|
|
203
|
+
.a {}
|
|
194
204
|
.a {} /* duplicate class selector */
|
|
195
205
|
`,
|
|
196
206
|
'/test/2.css': dedent`
|
|
197
207
|
.a {} /* class selector that duplicates the import source */
|
|
198
208
|
.b {}
|
|
199
209
|
`,
|
|
200
|
-
'/test/3.css': dedent`
|
|
201
|
-
.c {}
|
|
202
|
-
`,
|
|
203
210
|
});
|
|
204
211
|
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
205
212
|
expect(result).toMatchInlineSnapshot(`
|
|
206
213
|
{
|
|
207
|
-
dependencies: ["<fixtures>/test/2.css"
|
|
214
|
+
dependencies: ["<fixtures>/test/2.css"],
|
|
208
215
|
tokens: [
|
|
209
216
|
{
|
|
210
217
|
name: "a",
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
218
|
+
originalLocation: {
|
|
219
|
+
filePath: "<fixtures>/test/2.css",
|
|
220
|
+
start: { line: 1, column: 1 },
|
|
221
|
+
end: { line: 1, column: 2 },
|
|
222
|
+
},
|
|
216
223
|
},
|
|
217
224
|
{
|
|
218
225
|
name: "b",
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
226
|
+
originalLocation: {
|
|
227
|
+
filePath: "<fixtures>/test/2.css",
|
|
228
|
+
start: { line: 2, column: 1 },
|
|
229
|
+
end: { line: 2, column: 2 },
|
|
230
|
+
},
|
|
222
231
|
},
|
|
223
232
|
{
|
|
224
|
-
name: "
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
233
|
+
name: "a",
|
|
234
|
+
originalLocation: {
|
|
235
|
+
filePath: "<fixtures>/test/1.css",
|
|
236
|
+
start: { line: 4, column: 1 },
|
|
237
|
+
end: { line: 4, column: 2 },
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: "a",
|
|
242
|
+
originalLocation: {
|
|
243
|
+
filePath: "<fixtures>/test/1.css",
|
|
244
|
+
start: { line: 5, column: 1 },
|
|
245
|
+
end: { line: 5, column: 2 },
|
|
246
|
+
},
|
|
228
247
|
},
|
|
229
248
|
],
|
|
230
249
|
}
|
|
231
250
|
`);
|
|
232
251
|
});
|
|
233
252
|
|
|
234
|
-
test
|
|
253
|
+
test('returns the result from the cache when the file has not been modified', async () => {
|
|
235
254
|
createFixtures({
|
|
236
255
|
'/test/1.css': dedent`
|
|
237
256
|
@import './2.css';
|
|
238
|
-
@import './
|
|
239
|
-
.a {
|
|
240
|
-
composes: b from './2.css';
|
|
241
|
-
composes: c from './3.css';
|
|
242
|
-
composes: d from './3.css';
|
|
243
|
-
}
|
|
257
|
+
@import './3.css';
|
|
244
258
|
`,
|
|
245
259
|
'/test/2.css': dedent`
|
|
246
260
|
.b {}
|
|
@@ -250,12 +264,14 @@ test.failing('returns the result from the cache when the file has not been modif
|
|
|
250
264
|
.d {}
|
|
251
265
|
`,
|
|
252
266
|
});
|
|
267
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
268
|
+
const readCSSSpy = jest.spyOn(locator, 'readCSS' as any);
|
|
253
269
|
await locator.load(getFixturePath('/test/1.css'));
|
|
254
|
-
expect(
|
|
255
|
-
expect(
|
|
256
|
-
expect(
|
|
257
|
-
expect(
|
|
258
|
-
|
|
270
|
+
expect(readCSSSpy).toHaveBeenCalledTimes(3);
|
|
271
|
+
expect(readCSSSpy).toHaveBeenNthCalledWith(1, getFixturePath('/test/1.css'));
|
|
272
|
+
expect(readCSSSpy).toHaveBeenNthCalledWith(2, getFixturePath('/test/2.css'));
|
|
273
|
+
expect(readCSSSpy).toHaveBeenNthCalledWith(3, getFixturePath('/test/3.css'));
|
|
274
|
+
readCSSSpy.mockClear();
|
|
259
275
|
|
|
260
276
|
// update `/test/2.css`
|
|
261
277
|
sleepSync(1); // wait for the file system to update the mtime
|
|
@@ -263,54 +279,17 @@ test.failing('returns the result from the cache when the file has not been modif
|
|
|
263
279
|
|
|
264
280
|
// `3.css` is not updated, so the cache is used. Therefore, `readFile` is not called.
|
|
265
281
|
await locator.load(getFixturePath('/test/3.css'));
|
|
266
|
-
expect(
|
|
282
|
+
expect(readCSSSpy).toHaveBeenCalledTimes(0);
|
|
267
283
|
|
|
268
284
|
// `1.css` is not updated, but dependencies are updated, so the cache is used. Therefore, `readFile` is called.
|
|
269
285
|
await locator.load(getFixturePath('/test/1.css'));
|
|
270
|
-
expect(
|
|
271
|
-
expect(
|
|
272
|
-
expect(
|
|
286
|
+
expect(readCSSSpy).toHaveBeenCalledTimes(2);
|
|
287
|
+
expect(readCSSSpy).toHaveBeenNthCalledWith(1, getFixturePath('/test/1.css'));
|
|
288
|
+
expect(readCSSSpy).toHaveBeenNthCalledWith(2, getFixturePath('/test/2.css'));
|
|
273
289
|
|
|
274
290
|
// ``2.css` is updated, but the cache is already available because it was updated in the previous step. Therefore, `readFile` is not called.
|
|
275
291
|
await locator.load(getFixturePath('/test/2.css'));
|
|
276
|
-
expect(
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
test('ignores the composition of non-existent tokens', async () => {
|
|
280
|
-
// In css-loader and postcss-modules, compositions of non-existent tokens are simply ignored.
|
|
281
|
-
// Therefore, happy-css-modules follows suit.
|
|
282
|
-
// It may be preferable to warn rather than ignore, but for now, we will focus on compatibility.
|
|
283
|
-
// ref: https://github.com/css-modules/css-modules/issues/356
|
|
284
|
-
createFixtures({
|
|
285
|
-
'/test/1.css': dedent`
|
|
286
|
-
.a {
|
|
287
|
-
composes: b c from './2.css';
|
|
288
|
-
}
|
|
289
|
-
`,
|
|
290
|
-
'/test/2.css': dedent`
|
|
291
|
-
.b {}
|
|
292
|
-
`,
|
|
293
|
-
});
|
|
294
|
-
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
295
|
-
expect(result.tokens.map((t) => t.name)).toStrictEqual(['a', 'b']);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test('throws error the composition of non-existent file', async () => {
|
|
299
|
-
// In postcss-modules, compositions of non-existent file are causes an error.
|
|
300
|
-
// Therefore, happy-css-modules follows suit.
|
|
301
|
-
createFixtures({
|
|
302
|
-
'/test/1.css': dedent`
|
|
303
|
-
.a {
|
|
304
|
-
composes: a from './2.css';
|
|
305
|
-
}
|
|
306
|
-
`,
|
|
307
|
-
});
|
|
308
|
-
await expect(async () => {
|
|
309
|
-
await locator.load(getFixturePath('/test/1.css')).catch((e) => {
|
|
310
|
-
e.message = e.message.replace(FIXTURE_DIR_PATH, '<fixtures>');
|
|
311
|
-
throw e;
|
|
312
|
-
});
|
|
313
|
-
}).rejects.toThrowError(`Could not resolve './2.css' in '<fixtures>/test/1.css'`);
|
|
292
|
+
expect(readCSSSpy).toHaveBeenCalledTimes(2);
|
|
314
293
|
});
|
|
315
294
|
|
|
316
295
|
describe('supports sourcemap', () => {
|
|
@@ -334,16 +313,27 @@ describe('supports sourcemap', () => {
|
|
|
334
313
|
tokens: [
|
|
335
314
|
{
|
|
336
315
|
name: "nesting",
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
316
|
+
originalLocation: {
|
|
317
|
+
filePath: "<fixtures>/test/1.scss",
|
|
318
|
+
start: { line: 1, column: 1 },
|
|
319
|
+
end: { line: 1, column: 8 },
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: "nesting",
|
|
324
|
+
originalLocation: {
|
|
325
|
+
filePath: "<fixtures>/test/1.scss",
|
|
326
|
+
start: { line: 3, column: 3 },
|
|
327
|
+
end: { line: 3, column: 10 },
|
|
328
|
+
},
|
|
341
329
|
},
|
|
342
330
|
{
|
|
343
331
|
name: "nesting_child",
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
332
|
+
originalLocation: {
|
|
333
|
+
filePath: "<fixtures>/test/1.scss",
|
|
334
|
+
start: { line: 3, column: 3 },
|
|
335
|
+
end: { line: 3, column: 16 },
|
|
336
|
+
},
|
|
347
337
|
},
|
|
348
338
|
],
|
|
349
339
|
}
|
|
@@ -373,18 +363,22 @@ describe('supports sourcemap', () => {
|
|
|
373
363
|
tokens: [
|
|
374
364
|
{
|
|
375
365
|
name: "selector_list_a_1",
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
366
|
+
originalLocation: {
|
|
367
|
+
filePath: "<fixtures>/test/1.css",
|
|
368
|
+
start: { line: 1, column: 1 },
|
|
369
|
+
end: { line: 1, column: 18 },
|
|
370
|
+
},
|
|
379
371
|
},
|
|
380
372
|
{
|
|
381
373
|
name: "selector_list_a_2",
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
374
|
+
originalLocation: {
|
|
375
|
+
filePath: "<fixtures>/test/1.css",
|
|
376
|
+
start: { line: 1, column: 1 },
|
|
377
|
+
end: { line: 1, column: 18 },
|
|
378
|
+
},
|
|
385
379
|
},
|
|
386
|
-
{ name: "selector_list_b_1",
|
|
387
|
-
{ name: "selector_list_b_2",
|
|
380
|
+
{ name: "selector_list_b_1", originalLocation: {} },
|
|
381
|
+
{ name: "selector_list_b_2", originalLocation: {} },
|
|
388
382
|
],
|
|
389
383
|
}
|
|
390
384
|
`);
|
package/src/locator/index.ts
CHANGED
|
@@ -5,12 +5,13 @@ 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
7
|
import {
|
|
8
|
-
|
|
8
|
+
getOriginalLocationOfClassSelector,
|
|
9
|
+
getOriginalLocationOfAtValue,
|
|
9
10
|
generateLocalTokenNames,
|
|
10
11
|
parseAtImport,
|
|
11
12
|
type Location,
|
|
12
|
-
parseComposesDeclarationWithFromUrl,
|
|
13
13
|
collectNodes,
|
|
14
|
+
parseAtValue,
|
|
14
15
|
} from './postcss.js';
|
|
15
16
|
|
|
16
17
|
export { collectNodes, type Location } from './postcss.js';
|
|
@@ -27,8 +28,10 @@ function isIgnoredSpecifier(specifier: string): boolean {
|
|
|
27
28
|
export type Token = {
|
|
28
29
|
/** The token name. */
|
|
29
30
|
name: string;
|
|
30
|
-
/** The
|
|
31
|
-
|
|
31
|
+
/** The name of the imported token. */
|
|
32
|
+
importedName?: string;
|
|
33
|
+
/** The original location of the token in the source file. */
|
|
34
|
+
originalLocation: Location;
|
|
32
35
|
};
|
|
33
36
|
|
|
34
37
|
type CacheEntry = {
|
|
@@ -38,28 +41,12 @@ type CacheEntry = {
|
|
|
38
41
|
|
|
39
42
|
/** The result of `Locator#load`. */
|
|
40
43
|
export type LoadResult = {
|
|
41
|
-
/** The path of the file imported from the source file with `@import
|
|
44
|
+
/** The path of the file imported from the source file with `@import`. */
|
|
42
45
|
dependencies: string[];
|
|
43
46
|
/** The tokens exported by the source file. */
|
|
44
47
|
tokens: Token[];
|
|
45
48
|
};
|
|
46
49
|
|
|
47
|
-
function normalizeTokens(tokens: Token[]): Token[] {
|
|
48
|
-
const tokenNameToOriginalLocations = new Map<string, Location[]>();
|
|
49
|
-
for (const token of tokens) {
|
|
50
|
-
tokenNameToOriginalLocations.set(
|
|
51
|
-
token.name,
|
|
52
|
-
uniqueBy([...(tokenNameToOriginalLocations.get(token.name) ?? []), ...token.originalLocations], (location) =>
|
|
53
|
-
JSON.stringify(location),
|
|
54
|
-
),
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
return Array.from(tokenNameToOriginalLocations.entries()).map(([name, originalLocations]) => ({
|
|
58
|
-
name,
|
|
59
|
-
originalLocations,
|
|
60
|
-
}));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
50
|
export type LocatorOptions = {
|
|
64
51
|
/** The function to transform source code. */
|
|
65
52
|
transformer?: Transformer | undefined;
|
|
@@ -165,7 +152,7 @@ export class Locator {
|
|
|
165
152
|
|
|
166
153
|
const tokens: Token[] = [];
|
|
167
154
|
|
|
168
|
-
const { atImports,
|
|
155
|
+
const { atImports, atValues, classSelectors } = collectNodes(ast);
|
|
169
156
|
|
|
170
157
|
// Load imported sheets recursively.
|
|
171
158
|
for (const atImport of atImports) {
|
|
@@ -187,31 +174,51 @@ export class Locator {
|
|
|
187
174
|
// NOTE: This method has false positives. However, it works as expected in many cases.
|
|
188
175
|
if (!localTokenNames.includes(classSelector.value)) continue;
|
|
189
176
|
|
|
190
|
-
const originalLocation =
|
|
177
|
+
const originalLocation = getOriginalLocationOfClassSelector(rule, classSelector);
|
|
191
178
|
|
|
192
179
|
tokens.push({
|
|
193
180
|
name: classSelector.value,
|
|
194
|
-
|
|
181
|
+
originalLocation,
|
|
195
182
|
});
|
|
196
183
|
}
|
|
197
184
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
185
|
+
for (const atValue of atValues) {
|
|
186
|
+
const parsedAtValue = parseAtValue(atValue);
|
|
187
|
+
|
|
188
|
+
if (parsedAtValue.type === 'valueDeclaration') {
|
|
189
|
+
tokens.push({
|
|
190
|
+
name: parsedAtValue.tokenName,
|
|
191
|
+
originalLocation: getOriginalLocationOfAtValue(atValue, parsedAtValue),
|
|
192
|
+
});
|
|
193
|
+
} else if (parsedAtValue.type === 'valueImportDeclaration') {
|
|
194
|
+
if (isIgnoredSpecifier(parsedAtValue.from)) continue;
|
|
195
|
+
// eslint-disable-next-line no-await-in-loop
|
|
196
|
+
const from = await this.resolver(parsedAtValue.from, { request: filePath });
|
|
197
|
+
// eslint-disable-next-line no-await-in-loop
|
|
198
|
+
const result = await this._load(from);
|
|
199
|
+
dependencies.push(from, ...result.dependencies);
|
|
200
|
+
for (const token of result.tokens) {
|
|
201
|
+
const matchedImport = parsedAtValue.imports.find((i) => i.importedTokenName === token.name);
|
|
202
|
+
if (!matchedImport) continue;
|
|
203
|
+
if (matchedImport.localTokenName === matchedImport.importedTokenName) {
|
|
204
|
+
tokens.push({
|
|
205
|
+
name: matchedImport.localTokenName,
|
|
206
|
+
originalLocation: token.originalLocation,
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
tokens.push({
|
|
210
|
+
name: matchedImport.localTokenName,
|
|
211
|
+
importedName: matchedImport.importedTokenName,
|
|
212
|
+
originalLocation: token.originalLocation,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
210
217
|
}
|
|
211
218
|
|
|
212
219
|
const result: LoadResult = {
|
|
213
220
|
dependencies: unique(dependencies).filter((dep) => dep !== filePath),
|
|
214
|
-
tokens:
|
|
221
|
+
tokens: uniqueBy(tokens, (token) => JSON.stringify(token)),
|
|
215
222
|
};
|
|
216
223
|
this.cache.set(filePath, { mtime, result });
|
|
217
224
|
return result;
|