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,17 +1,19 @@
|
|
|
1
1
|
import dedent from 'dedent';
|
|
2
|
+
import type { AtRule } from 'postcss';
|
|
2
3
|
import {
|
|
3
4
|
createRoot,
|
|
4
5
|
createClassSelectors,
|
|
5
6
|
createAtImports,
|
|
6
|
-
createComposesDeclarations,
|
|
7
7
|
createFixtures,
|
|
8
|
+
createAtValues,
|
|
8
9
|
} from '../test-util/util.js';
|
|
9
10
|
import {
|
|
10
11
|
generateLocalTokenNames,
|
|
11
|
-
|
|
12
|
+
getOriginalLocationOfClassSelector,
|
|
12
13
|
parseAtImport,
|
|
13
|
-
|
|
14
|
+
parseAtValue,
|
|
14
15
|
collectNodes,
|
|
16
|
+
getOriginalLocationOfAtValue,
|
|
15
17
|
} from './postcss.js';
|
|
16
18
|
|
|
17
19
|
describe('generateLocalTokenNames', () => {
|
|
@@ -39,13 +41,11 @@ describe('generateLocalTokenNames', () => {
|
|
|
39
41
|
.local_class_name_3 {}
|
|
40
42
|
}
|
|
41
43
|
:local(.local_class_name_4) {}
|
|
42
|
-
|
|
43
|
-
.composes {
|
|
44
|
-
composes: composes_target;
|
|
45
|
-
}
|
|
44
|
+
@value value: #BF4040;
|
|
46
45
|
`),
|
|
47
46
|
),
|
|
48
47
|
).toStrictEqual([
|
|
48
|
+
'value',
|
|
49
49
|
'basic',
|
|
50
50
|
'cascading',
|
|
51
51
|
'pseudo_class_1',
|
|
@@ -62,8 +62,6 @@ describe('generateLocalTokenNames', () => {
|
|
|
62
62
|
'local_class_name_2',
|
|
63
63
|
'local_class_name_3',
|
|
64
64
|
'local_class_name_4',
|
|
65
|
-
'composes_target',
|
|
66
|
-
'composes',
|
|
67
65
|
]);
|
|
68
66
|
});
|
|
69
67
|
test('does not track styles imported by @import in other file because it is not a local token', async () => {
|
|
@@ -81,14 +79,18 @@ describe('generateLocalTokenNames', () => {
|
|
|
81
79
|
).toStrictEqual([]);
|
|
82
80
|
});
|
|
83
81
|
test('does not track styles imported by @value in other file because it is not a local token', async () => {
|
|
84
|
-
createFixtures({
|
|
82
|
+
createFixtures({
|
|
83
|
+
'/test/1.css': dedent`
|
|
84
|
+
.a {}
|
|
85
|
+
`,
|
|
86
|
+
});
|
|
85
87
|
expect(
|
|
86
88
|
await generateLocalTokenNames(
|
|
87
89
|
createRoot(`
|
|
88
|
-
@value
|
|
90
|
+
@value a from "/test/1.css";
|
|
89
91
|
`),
|
|
90
92
|
),
|
|
91
|
-
).toStrictEqual([]);
|
|
93
|
+
).toStrictEqual(['a']);
|
|
92
94
|
});
|
|
93
95
|
test('does not track styles imported by composes in other file because it is not a local token', async () => {
|
|
94
96
|
createFixtures({
|
|
@@ -108,14 +110,14 @@ describe('generateLocalTokenNames', () => {
|
|
|
108
110
|
});
|
|
109
111
|
});
|
|
110
112
|
|
|
111
|
-
describe('
|
|
113
|
+
describe('getOriginalLocationOfClassSelector', () => {
|
|
112
114
|
test('basic', () => {
|
|
113
115
|
const [basic] = createClassSelectors(
|
|
114
116
|
createRoot(dedent`
|
|
115
117
|
.basic {}
|
|
116
118
|
`),
|
|
117
119
|
);
|
|
118
|
-
expect(
|
|
120
|
+
expect(getOriginalLocationOfClassSelector(basic!.rule, basic!.classSelector)).toMatchInlineSnapshot(
|
|
119
121
|
`{ filePath: "/test/test.css", start: { line: 1, column: 1 }, end: { line: 1, column: 6 } }`,
|
|
120
122
|
);
|
|
121
123
|
});
|
|
@@ -127,10 +129,10 @@ describe('getOriginalLocation', () => {
|
|
|
127
129
|
.cascading {}
|
|
128
130
|
`),
|
|
129
131
|
);
|
|
130
|
-
expect(
|
|
132
|
+
expect(getOriginalLocationOfClassSelector(cascading_1!.rule, cascading_1!.classSelector)).toMatchInlineSnapshot(
|
|
131
133
|
`{ filePath: "/test/test.css", start: { line: 1, column: 1 }, end: { line: 1, column: 10 } }`,
|
|
132
134
|
);
|
|
133
|
-
expect(
|
|
135
|
+
expect(getOriginalLocationOfClassSelector(cascading_2!.rule, cascading_2!.classSelector)).toMatchInlineSnapshot(
|
|
134
136
|
`{ filePath: "/test/test.css", start: { line: 2, column: 1 }, end: { line: 2, column: 10 } }`,
|
|
135
137
|
);
|
|
136
138
|
});
|
|
@@ -143,13 +145,19 @@ describe('getOriginalLocation', () => {
|
|
|
143
145
|
:not(.pseudo_class_3) {}
|
|
144
146
|
`),
|
|
145
147
|
);
|
|
146
|
-
expect(
|
|
148
|
+
expect(
|
|
149
|
+
getOriginalLocationOfClassSelector(pseudo_class_1!.rule, pseudo_class_1!.classSelector),
|
|
150
|
+
).toMatchInlineSnapshot(
|
|
147
151
|
`{ filePath: "/test/test.css", start: { line: 1, column: 1 }, end: { line: 1, column: 15 } }`,
|
|
148
152
|
);
|
|
149
|
-
expect(
|
|
153
|
+
expect(
|
|
154
|
+
getOriginalLocationOfClassSelector(pseudo_class_2!.rule, pseudo_class_2!.classSelector),
|
|
155
|
+
).toMatchInlineSnapshot(
|
|
150
156
|
`{ filePath: "/test/test.css", start: { line: 2, column: 1 }, end: { line: 2, column: 15 } }`,
|
|
151
157
|
);
|
|
152
|
-
expect(
|
|
158
|
+
expect(
|
|
159
|
+
getOriginalLocationOfClassSelector(pseudo_class_3!.rule, pseudo_class_3!.classSelector),
|
|
160
|
+
).toMatchInlineSnapshot(
|
|
153
161
|
`{ filePath: "/test/test.css", start: { line: 3, column: 6 }, end: { line: 3, column: 20 } }`,
|
|
154
162
|
);
|
|
155
163
|
});
|
|
@@ -160,10 +168,14 @@ describe('getOriginalLocation', () => {
|
|
|
160
168
|
.multiple_selector_1.multiple_selector_2 {}
|
|
161
169
|
`),
|
|
162
170
|
);
|
|
163
|
-
expect(
|
|
171
|
+
expect(
|
|
172
|
+
getOriginalLocationOfClassSelector(multiple_selector_1!.rule, multiple_selector_1!.classSelector),
|
|
173
|
+
).toMatchInlineSnapshot(
|
|
164
174
|
`{ filePath: "/test/test.css", start: { line: 1, column: 1 }, end: { line: 1, column: 20 } }`,
|
|
165
175
|
);
|
|
166
|
-
expect(
|
|
176
|
+
expect(
|
|
177
|
+
getOriginalLocationOfClassSelector(multiple_selector_2!.rule, multiple_selector_2!.classSelector),
|
|
178
|
+
).toMatchInlineSnapshot(
|
|
167
179
|
`{ filePath: "/test/test.css", start: { line: 1, column: 21 }, end: { line: 1, column: 40 } }`,
|
|
168
180
|
);
|
|
169
181
|
});
|
|
@@ -175,10 +187,10 @@ describe('getOriginalLocation', () => {
|
|
|
175
187
|
.combinator_1 + .combinator_2 {}
|
|
176
188
|
`),
|
|
177
189
|
);
|
|
178
|
-
expect(
|
|
190
|
+
expect(getOriginalLocationOfClassSelector(combinator_1!.rule, combinator_1!.classSelector)).toMatchInlineSnapshot(
|
|
179
191
|
`{ filePath: "/test/test.css", start: { line: 1, column: 1 }, end: { line: 1, column: 13 } }`,
|
|
180
192
|
);
|
|
181
|
-
expect(
|
|
193
|
+
expect(getOriginalLocationOfClassSelector(combinator_2!.rule, combinator_2!.classSelector)).toMatchInlineSnapshot(
|
|
182
194
|
`{ filePath: "/test/test.css", start: { line: 1, column: 17 }, end: { line: 1, column: 29 } }`,
|
|
183
195
|
);
|
|
184
196
|
});
|
|
@@ -193,7 +205,7 @@ describe('getOriginalLocation', () => {
|
|
|
193
205
|
}
|
|
194
206
|
`),
|
|
195
207
|
);
|
|
196
|
-
expect(
|
|
208
|
+
expect(getOriginalLocationOfClassSelector(at_rule!.rule, at_rule!.classSelector)).toMatchInlineSnapshot(
|
|
197
209
|
`{ filePath: "/test/test.css", start: { line: 3, column: 5 }, end: { line: 3, column: 12 } }`,
|
|
198
210
|
);
|
|
199
211
|
});
|
|
@@ -204,10 +216,14 @@ describe('getOriginalLocation', () => {
|
|
|
204
216
|
.selector_list_1, .selector_list_2 {}
|
|
205
217
|
`),
|
|
206
218
|
);
|
|
207
|
-
expect(
|
|
219
|
+
expect(
|
|
220
|
+
getOriginalLocationOfClassSelector(selector_list_1!.rule, selector_list_1!.classSelector),
|
|
221
|
+
).toMatchInlineSnapshot(
|
|
208
222
|
`{ filePath: "/test/test.css", start: { line: 1, column: 1 }, end: { line: 1, column: 16 } }`,
|
|
209
223
|
);
|
|
210
|
-
expect(
|
|
224
|
+
expect(
|
|
225
|
+
getOriginalLocationOfClassSelector(selector_list_2!.rule, selector_list_2!.classSelector),
|
|
226
|
+
).toMatchInlineSnapshot(
|
|
211
227
|
`{ filePath: "/test/test.css", start: { line: 1, column: 19 }, end: { line: 1, column: 34 } }`,
|
|
212
228
|
);
|
|
213
229
|
});
|
|
@@ -223,36 +239,27 @@ describe('getOriginalLocation', () => {
|
|
|
223
239
|
:local(.local_class_name_4) {}
|
|
224
240
|
`),
|
|
225
241
|
);
|
|
226
|
-
expect(
|
|
242
|
+
expect(
|
|
243
|
+
getOriginalLocationOfClassSelector(local_class_name_1!.rule, local_class_name_1!.classSelector),
|
|
244
|
+
).toMatchInlineSnapshot(
|
|
227
245
|
`{ filePath: "/test/test.css", start: { line: 1, column: 8 }, end: { line: 1, column: 26 } }`,
|
|
228
246
|
);
|
|
229
|
-
expect(
|
|
247
|
+
expect(
|
|
248
|
+
getOriginalLocationOfClassSelector(local_class_name_2!.rule, local_class_name_2!.classSelector),
|
|
249
|
+
).toMatchInlineSnapshot(
|
|
230
250
|
`{ filePath: "/test/test.css", start: { line: 3, column: 3 }, end: { line: 3, column: 21 } }`,
|
|
231
251
|
);
|
|
232
|
-
expect(
|
|
252
|
+
expect(
|
|
253
|
+
getOriginalLocationOfClassSelector(local_class_name_3!.rule, local_class_name_3!.classSelector),
|
|
254
|
+
).toMatchInlineSnapshot(
|
|
233
255
|
`{ filePath: "/test/test.css", start: { line: 4, column: 3 }, end: { line: 4, column: 21 } }`,
|
|
234
256
|
);
|
|
235
|
-
expect(
|
|
257
|
+
expect(
|
|
258
|
+
getOriginalLocationOfClassSelector(local_class_name_4!.rule, local_class_name_4!.classSelector),
|
|
259
|
+
).toMatchInlineSnapshot(
|
|
236
260
|
`{ filePath: "/test/test.css", start: { line: 6, column: 8 }, end: { line: 6, column: 26 } }`,
|
|
237
261
|
);
|
|
238
262
|
});
|
|
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
263
|
test('with_newline', () => {
|
|
257
264
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
258
265
|
const [with_newline_1, with_newline_2, with_newline_3] = createClassSelectors(
|
|
@@ -262,41 +269,63 @@ describe('getOriginalLocation', () => {
|
|
|
262
269
|
+ .with_newline_3, {}
|
|
263
270
|
`),
|
|
264
271
|
);
|
|
265
|
-
expect(
|
|
272
|
+
expect(
|
|
273
|
+
getOriginalLocationOfClassSelector(with_newline_1!.rule, with_newline_1!.classSelector),
|
|
274
|
+
).toMatchInlineSnapshot(
|
|
266
275
|
`{ filePath: "/test/test.css", start: { line: 1, column: 1 }, end: { line: 1, column: 15 } }`,
|
|
267
276
|
);
|
|
268
|
-
expect(
|
|
277
|
+
expect(
|
|
278
|
+
getOriginalLocationOfClassSelector(with_newline_2!.rule, with_newline_2!.classSelector),
|
|
279
|
+
).toMatchInlineSnapshot(
|
|
269
280
|
`{ filePath: "/test/test.css", start: { line: 2, column: 1 }, end: { line: 2, column: 15 } }`,
|
|
270
281
|
);
|
|
271
282
|
|
|
272
|
-
expect(
|
|
283
|
+
expect(
|
|
284
|
+
getOriginalLocationOfClassSelector(with_newline_3!.rule, with_newline_3!.classSelector),
|
|
285
|
+
).toMatchInlineSnapshot(
|
|
273
286
|
`{ filePath: "/test/test.css", start: { line: 3, column: 5 }, end: { line: 3, column: 19 } }`,
|
|
274
287
|
);
|
|
275
288
|
});
|
|
276
289
|
});
|
|
277
290
|
|
|
291
|
+
test('getOriginalLocationOfAtValue', () => {
|
|
292
|
+
function tryGetOriginalLocationOfAtValue(atValue: AtRule) {
|
|
293
|
+
const parsed = parseAtValue(atValue);
|
|
294
|
+
if (parsed.type === 'valueDeclaration') {
|
|
295
|
+
return getOriginalLocationOfAtValue(atValue, parsed);
|
|
296
|
+
} else {
|
|
297
|
+
throw new Error('Unexpected type');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const [basic] = createAtValues(
|
|
301
|
+
createRoot(dedent`
|
|
302
|
+
@value basic: #000;
|
|
303
|
+
`),
|
|
304
|
+
);
|
|
305
|
+
expect(tryGetOriginalLocationOfAtValue(basic!)).toMatchInlineSnapshot(
|
|
306
|
+
`{ filePath: "/test/test.css", start: { line: 1, column: 8 }, end: { line: 1, column: 13 } }`,
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
|
|
278
310
|
test('collectNodes', () => {
|
|
279
311
|
const ast = createRoot(dedent`
|
|
280
312
|
@import;
|
|
281
313
|
@import "test.css";
|
|
282
314
|
@ignored;
|
|
283
|
-
.a { ignored: "ignored";
|
|
284
|
-
.b { ignored: "ignored";
|
|
315
|
+
.a { ignored: "ignored"; }
|
|
316
|
+
.b { ignored: "ignored"; }
|
|
285
317
|
`);
|
|
286
318
|
|
|
287
|
-
const { atImports, classSelectors
|
|
319
|
+
const { atImports, classSelectors } = collectNodes(ast);
|
|
288
320
|
|
|
289
321
|
expect(atImports).toHaveLength(2);
|
|
290
322
|
expect(atImports[0]!.toString()).toEqual('@import');
|
|
291
323
|
expect(atImports[1]!.toString()).toEqual('@import "test.css"');
|
|
292
324
|
expect(classSelectors).toHaveLength(2);
|
|
293
|
-
expect(classSelectors[0]!.rule.toString()).toEqual('.a { ignored: "ignored";
|
|
325
|
+
expect(classSelectors[0]!.rule.toString()).toEqual('.a { ignored: "ignored"; }');
|
|
294
326
|
expect(classSelectors[0]!.classSelector.toString()).toEqual('.a');
|
|
295
|
-
expect(classSelectors[1]!.rule.toString()).toEqual('.b { ignored: "ignored";
|
|
327
|
+
expect(classSelectors[1]!.rule.toString()).toEqual('.b { ignored: "ignored"; }');
|
|
296
328
|
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
329
|
});
|
|
301
330
|
|
|
302
331
|
test('parseAtImport', () => {
|
|
@@ -316,31 +345,44 @@ test('parseAtImport', () => {
|
|
|
316
345
|
expect(parseAtImport(atImports[4]!)).toBe('test.css');
|
|
317
346
|
});
|
|
318
347
|
|
|
319
|
-
test('
|
|
320
|
-
const
|
|
348
|
+
test('parseAtValue', () => {
|
|
349
|
+
const atValues = createAtValues(
|
|
321
350
|
createRoot(dedent`
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
351
|
+
@value basic: #000;
|
|
352
|
+
@value withoutColon #000;
|
|
353
|
+
@value empty:;
|
|
354
|
+
@value comment:/* comment */;
|
|
355
|
+
@value complex: (max-width: 599px);
|
|
356
|
+
@value import from "test.css";
|
|
357
|
+
@value import1, import2 from "test.css";
|
|
358
|
+
@value import as alias from "test.css";
|
|
359
|
+
/*
|
|
360
|
+
* NOTE: happy-css-modules intentionally does not support module specifier as variable.
|
|
361
|
+
* e.g. \`@value d, e from moduleName;\`
|
|
362
|
+
*/
|
|
330
363
|
`),
|
|
331
364
|
);
|
|
332
|
-
expect(
|
|
333
|
-
expect(
|
|
334
|
-
expect(
|
|
365
|
+
expect(parseAtValue(atValues[0]!)).toStrictEqual({ type: 'valueDeclaration', tokenName: 'basic' });
|
|
366
|
+
expect(parseAtValue(atValues[1]!)).toStrictEqual({ type: 'valueDeclaration', tokenName: 'withoutColon' });
|
|
367
|
+
expect(parseAtValue(atValues[2]!)).toStrictEqual({ type: 'valueDeclaration', tokenName: 'empty' });
|
|
368
|
+
expect(parseAtValue(atValues[3]!)).toStrictEqual({ type: 'valueDeclaration', tokenName: 'comment' });
|
|
369
|
+
expect(parseAtValue(atValues[4]!)).toStrictEqual({ type: 'valueDeclaration', tokenName: 'complex' });
|
|
370
|
+
expect(parseAtValue(atValues[5]!)).toStrictEqual({
|
|
371
|
+
type: 'valueImportDeclaration',
|
|
372
|
+
imports: [{ importedTokenName: 'import', localTokenName: 'import' }],
|
|
335
373
|
from: 'test.css',
|
|
336
|
-
tokenNames: ['a'],
|
|
337
374
|
});
|
|
338
|
-
expect(
|
|
375
|
+
expect(parseAtValue(atValues[6]!)).toStrictEqual({
|
|
376
|
+
type: 'valueImportDeclaration',
|
|
377
|
+
imports: [
|
|
378
|
+
{ importedTokenName: 'import1', localTokenName: 'import1' },
|
|
379
|
+
{ importedTokenName: 'import2', localTokenName: 'import2' },
|
|
380
|
+
],
|
|
339
381
|
from: 'test.css',
|
|
340
|
-
tokenNames: ['a', 'b', 'c'],
|
|
341
382
|
});
|
|
342
|
-
expect(
|
|
383
|
+
expect(parseAtValue(atValues[7]!)).toStrictEqual({
|
|
384
|
+
type: 'valueImportDeclaration',
|
|
385
|
+
imports: [{ importedTokenName: 'import', localTokenName: 'alias' }],
|
|
343
386
|
from: 'test.css',
|
|
344
|
-
tokenNames: ['from', 'from', 'from'], // do not deduplicate.
|
|
345
387
|
});
|
|
346
388
|
});
|
package/src/locator/postcss.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import postcss, { type Rule, type AtRule, type Root, type Node
|
|
1
|
+
import postcss, { type Rule, type AtRule, type Root, type Node } from 'postcss';
|
|
2
2
|
import modules from 'postcss-modules';
|
|
3
3
|
import selectorParser, { type ClassName } from 'postcss-selector-parser';
|
|
4
4
|
import valueParser from 'postcss-value-parser';
|
|
@@ -26,40 +26,27 @@ export type Location =
|
|
|
26
26
|
end: undefined;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
function removeDependenciesPlugin(): Plugin {
|
|
30
|
-
return {
|
|
31
|
-
postcssPlugin: 'remove-dependencies',
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
33
|
-
AtRule(atRule) {
|
|
34
|
-
if (isAtImportNode(atRule) || isAtValueNode(atRule)) {
|
|
35
|
-
atRule.remove();
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
39
|
-
Declaration(declaration) {
|
|
40
|
-
if (isComposesDeclaration(declaration)) {
|
|
41
|
-
declaration.remove();
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
29
|
/**
|
|
48
30
|
* Traverses a local token from the AST and returns its name.
|
|
49
31
|
* @param ast The AST to traverse.
|
|
50
32
|
* @returns The name of the local token.
|
|
51
33
|
*/
|
|
52
34
|
export async function generateLocalTokenNames(ast: Root): Promise<string[]> {
|
|
35
|
+
class EmptyLoader {
|
|
36
|
+
async fetch(_file: string, _relativeTo: string, _depTrace: string): Promise<{ [key: string]: string }> {
|
|
37
|
+
// Return an empty object because we do not want to load external tokens in `generateLocalTokenNames`.
|
|
38
|
+
return Promise.resolve({});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
53
41
|
return new Promise((resolve, reject) => {
|
|
54
42
|
postcss
|
|
55
43
|
.default()
|
|
56
|
-
// postcss-modules collects tokens (i.e., includes external tokens) by following
|
|
57
|
-
// the dependencies specified in the @import and composes properties.
|
|
58
|
-
// However, we do not want `generateLocalTokenNames` to return external tokens.
|
|
59
|
-
// So we remove the @import and composes properties beforehand.
|
|
60
|
-
.use(removeDependenciesPlugin())
|
|
61
44
|
.use(
|
|
62
45
|
modules({
|
|
46
|
+
// `@import`, `@value`, and `composes` can read tokens from external files.
|
|
47
|
+
// However, we want to collect only local tokens. So we will fake that
|
|
48
|
+
// an empty token is exported from the external file.
|
|
49
|
+
Loader: EmptyLoader,
|
|
63
50
|
getJSON: (_cssFileName, json) => {
|
|
64
51
|
resolve(Object.keys(json));
|
|
65
52
|
},
|
|
@@ -72,12 +59,12 @@ export async function generateLocalTokenNames(ast: Root): Promise<string[]> {
|
|
|
72
59
|
}
|
|
73
60
|
|
|
74
61
|
/**
|
|
75
|
-
* Get the
|
|
62
|
+
* Get the original location of the class selector.
|
|
76
63
|
* @param rule The rule node that contains the token.
|
|
77
64
|
* @param classSelector The class selector node that contains the token.
|
|
78
|
-
* @returns The
|
|
65
|
+
* @returns The original location of the class selector.
|
|
79
66
|
*/
|
|
80
|
-
export function
|
|
67
|
+
export function getOriginalLocationOfClassSelector(rule: Rule, classSelector: ClassName): Location {
|
|
81
68
|
// The node derived from `postcss.parse` always has `source` property. Therefore, this line is unreachable.
|
|
82
69
|
if (rule.source === undefined || classSelector.source === undefined) throw new Error('Node#source is undefined');
|
|
83
70
|
// The node derived from `postcss.parse` always has `start` and `end` property. Therefore, this line is unreachable.
|
|
@@ -127,6 +114,32 @@ export function getOriginalLocation(rule: Rule, classSelector: ClassName): Locat
|
|
|
127
114
|
};
|
|
128
115
|
}
|
|
129
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Get the original location of `@value`.
|
|
119
|
+
* @param atValue The `@value` rule.
|
|
120
|
+
* @returns The location of the `@value` rule.
|
|
121
|
+
*/
|
|
122
|
+
export function getOriginalLocationOfAtValue(atValue: AtRule, valueDeclaration: ValueDeclaration): Location {
|
|
123
|
+
// The node derived from `postcss.parse` always has `source` property. Therefore, this line is unreachable.
|
|
124
|
+
if (atValue.source === undefined) throw new Error('Node#source is undefined');
|
|
125
|
+
// The node derived from `postcss.parse` always has `start` and `end` property. Therefore, this line is unreachable.
|
|
126
|
+
if (atValue.source.start === undefined) throw new Error('Node#start is undefined');
|
|
127
|
+
if (atValue.source.end === undefined) throw new Error('Node#end is undefined');
|
|
128
|
+
if (atValue.source.input.file === undefined) throw new Error('Node#input.file is undefined');
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
filePath: atValue.source.input.file,
|
|
132
|
+
start: {
|
|
133
|
+
line: atValue.source.start.line,
|
|
134
|
+
column: atValue.source.start.column + 7, // Add for `@value `
|
|
135
|
+
},
|
|
136
|
+
end: {
|
|
137
|
+
line: atValue.source.start.line,
|
|
138
|
+
column: atValue.source.start.column + 7 + valueDeclaration.tokenName.length, // Add for `@value ` and token name
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
130
143
|
function isAtRuleNode(node: Node): node is AtRule {
|
|
131
144
|
return node.type === 'atrule';
|
|
132
145
|
}
|
|
@@ -143,18 +156,10 @@ function isRuleNode(node: Node): node is Rule {
|
|
|
143
156
|
return node.type === 'rule';
|
|
144
157
|
}
|
|
145
158
|
|
|
146
|
-
function isDeclaration(node: Node): node is Declaration {
|
|
147
|
-
return node.type === 'decl';
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function isComposesDeclaration(node: Node): node is Declaration {
|
|
151
|
-
return isDeclaration(node) && node.prop === 'composes';
|
|
152
|
-
}
|
|
153
|
-
|
|
154
159
|
type CollectNodesResult = {
|
|
155
160
|
atImports: AtRule[];
|
|
161
|
+
atValues: AtRule[];
|
|
156
162
|
classSelectors: { rule: Rule; classSelector: ClassName }[];
|
|
157
|
-
composesDeclarations: Declaration[];
|
|
158
163
|
};
|
|
159
164
|
|
|
160
165
|
/**
|
|
@@ -163,11 +168,13 @@ type CollectNodesResult = {
|
|
|
163
168
|
*/
|
|
164
169
|
export function collectNodes(ast: Root): CollectNodesResult {
|
|
165
170
|
const atImports: AtRule[] = [];
|
|
171
|
+
const atValues: AtRule[] = [];
|
|
166
172
|
const classSelectors: { rule: Rule; classSelector: ClassName }[] = [];
|
|
167
|
-
const composesDeclarations: Declaration[] = [];
|
|
168
173
|
ast.walk((node) => {
|
|
169
174
|
if (isAtImportNode(node)) {
|
|
170
175
|
atImports.push(node);
|
|
176
|
+
} else if (isAtValueNode(node)) {
|
|
177
|
+
atValues.push(node);
|
|
171
178
|
} else if (isRuleNode(node)) {
|
|
172
179
|
// In `rule.selector` comes the following string:
|
|
173
180
|
// 1. ".foo"
|
|
@@ -183,11 +190,9 @@ export function collectNodes(ast: Root): CollectNodesResult {
|
|
|
183
190
|
}
|
|
184
191
|
});
|
|
185
192
|
}).processSync(node);
|
|
186
|
-
} else if (isComposesDeclaration(node)) {
|
|
187
|
-
composesDeclarations.push(node);
|
|
188
193
|
}
|
|
189
194
|
});
|
|
190
|
-
return { atImports,
|
|
195
|
+
return { atImports, atValues, classSelectors };
|
|
191
196
|
}
|
|
192
197
|
|
|
193
198
|
/**
|
|
@@ -207,39 +212,77 @@ export function parseAtImport(atImport: AtRule): string | undefined {
|
|
|
207
212
|
return undefined;
|
|
208
213
|
}
|
|
209
214
|
|
|
215
|
+
type ValueDeclaration = {
|
|
216
|
+
type: 'valueDeclaration';
|
|
217
|
+
tokenName: string;
|
|
218
|
+
// value: string; // unneeded
|
|
219
|
+
};
|
|
220
|
+
type ValueImportDeclaration = {
|
|
221
|
+
type: 'valueImportDeclaration';
|
|
222
|
+
imports: { importedTokenName: string; localTokenName: string }[];
|
|
223
|
+
from: string;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
type ParsedAtValue = ValueDeclaration | ValueImportDeclaration;
|
|
227
|
+
|
|
228
|
+
const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/u;
|
|
229
|
+
const matchValueDefinition = /(?:\s+|^)([\w-]+):?(.*?)$/u;
|
|
230
|
+
const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/u;
|
|
231
|
+
|
|
210
232
|
/**
|
|
211
|
-
* Parse
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* @
|
|
233
|
+
* Parse the `@value` rule.
|
|
234
|
+
* Forked from https://github.com/css-modules/postcss-modules-values/blob/v4.0.0/src/index.js.
|
|
235
|
+
*
|
|
236
|
+
* @license
|
|
237
|
+
* ISC License (ISC)
|
|
238
|
+
* Copyright (c) 2015, Glen Maddern
|
|
239
|
+
*
|
|
240
|
+
* Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
|
|
241
|
+
* provided that the above copyright notice and this permission notice appear in all copies.
|
|
242
|
+
*
|
|
243
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING
|
|
244
|
+
* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
245
|
+
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
246
|
+
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
|
|
247
|
+
* THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
215
248
|
*/
|
|
216
|
-
export function
|
|
217
|
-
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
249
|
+
export function parseAtValue(atValue: AtRule): ParsedAtValue {
|
|
250
|
+
const matchesForImports = atValue.params.match(matchImports);
|
|
251
|
+
if (matchesForImports) {
|
|
252
|
+
const [, aliases, path] = matchesForImports;
|
|
253
|
+
|
|
254
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
255
|
+
if (aliases === undefined || path === undefined) throw new Error(`unreachable`);
|
|
256
|
+
|
|
257
|
+
const imports = aliases
|
|
258
|
+
.replace(/^\(\s*([\s\S]+)\s*\)$/u, '$1')
|
|
259
|
+
.split(/\s*,\s*/u)
|
|
260
|
+
.map((alias) => {
|
|
261
|
+
const tokens = matchImport.exec(alias);
|
|
262
|
+
|
|
263
|
+
if (tokens) {
|
|
264
|
+
const [, theirName, myName] = tokens;
|
|
265
|
+
if (theirName === undefined) throw new Error(`unreachable`);
|
|
266
|
+
return { importedTokenName: theirName, localTokenName: myName ?? theirName };
|
|
267
|
+
} else {
|
|
268
|
+
throw new Error(`@import statement "${alias}" is invalid!`);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Remove quotes from the path.
|
|
273
|
+
// NOTE: This is a restriction unique to "happy-css-modules" and not a specification of CSS Modules.
|
|
274
|
+
const normalizedPath = path.replace(/^['"]|['"]$/gu, '');
|
|
275
|
+
|
|
276
|
+
return { type: 'valueImportDeclaration', imports, from: normalizedPath };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const matchesForValueDefinitions = `${atValue.params}${atValue.raws.between!}`.match(matchValueDefinition);
|
|
280
|
+
if (matchesForValueDefinitions) {
|
|
281
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
282
|
+
const [, key, value] = matchesForValueDefinitions;
|
|
283
|
+
if (key === undefined) throw new Error(`unreachable`);
|
|
284
|
+
return { type: 'valueDeclaration', tokenName: key };
|
|
285
|
+
}
|
|
286
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
287
|
+
throw new Error(`@value statement "${atValue.source!}" is invalid!`);
|
|
245
288
|
}
|