@css-modules-kit/core 0.7.0 → 0.8.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.
Files changed (60) hide show
  1. package/dist/checker.d.ts +9 -2
  2. package/dist/checker.d.ts.map +1 -1
  3. package/dist/checker.js +15 -14
  4. package/dist/checker.js.map +1 -1
  5. package/dist/config.d.ts +1 -0
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +30 -24
  8. package/dist/config.js.map +1 -1
  9. package/dist/diagnostic.d.ts.map +1 -1
  10. package/dist/diagnostic.js +9 -15
  11. package/dist/diagnostic.js.map +1 -1
  12. package/dist/dts-generator.d.ts +2 -6
  13. package/dist/dts-generator.d.ts.map +1 -1
  14. package/dist/dts-generator.js +211 -63
  15. package/dist/dts-generator.js.map +1 -1
  16. package/dist/error.js +2 -7
  17. package/dist/error.js.map +1 -1
  18. package/dist/export-builder.js +1 -4
  19. package/dist/export-builder.js.map +1 -1
  20. package/dist/file.js +17 -30
  21. package/dist/file.js.map +1 -1
  22. package/dist/index.d.ts +3 -3
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +12 -44
  25. package/dist/index.js.map +1 -1
  26. package/dist/parser/at-import-parser.js +3 -9
  27. package/dist/parser/at-import-parser.js.map +1 -1
  28. package/dist/parser/at-value-parser.js +1 -4
  29. package/dist/parser/at-value-parser.js.map +1 -1
  30. package/dist/parser/css-module-parser.js +15 -21
  31. package/dist/parser/css-module-parser.js.map +1 -1
  32. package/dist/parser/key-frame-parser.js +1 -4
  33. package/dist/parser/key-frame-parser.js.map +1 -1
  34. package/dist/parser/rule-parser.js +7 -13
  35. package/dist/parser/rule-parser.js.map +1 -1
  36. package/dist/path.d.ts +0 -1
  37. package/dist/path.d.ts.map +1 -1
  38. package/dist/path.js +16 -31
  39. package/dist/path.js.map +1 -1
  40. package/dist/resolver.d.ts.map +1 -1
  41. package/dist/resolver.js +13 -40
  42. package/dist/resolver.js.map +1 -1
  43. package/dist/type.d.ts +1 -1
  44. package/dist/type.d.ts.map +1 -1
  45. package/dist/type.js +1 -2
  46. package/dist/util.d.ts +1 -0
  47. package/dist/util.d.ts.map +1 -1
  48. package/dist/util.js +6 -8
  49. package/dist/util.js.map +1 -1
  50. package/package.json +2 -2
  51. package/src/checker.ts +19 -16
  52. package/src/config.ts +13 -0
  53. package/src/diagnostic.ts +2 -0
  54. package/src/dts-generator.ts +216 -66
  55. package/src/index.ts +3 -2
  56. package/src/path.ts +0 -3
  57. package/src/resolver.ts +7 -29
  58. package/src/type.ts +1 -1
  59. package/src/typing/typescript.d.ts +1 -1
  60. package/src/util.ts +4 -0
package/src/checker.ts CHANGED
@@ -4,23 +4,24 @@ import type {
4
4
  AtValueTokenImporterValue,
5
5
  CSSModule,
6
6
  Diagnostic,
7
- ExportBuilder,
7
+ ExportRecord,
8
8
  Location,
9
9
  MatchesPattern,
10
10
  Resolver,
11
11
  TokenImporter,
12
12
  } from './type.js';
13
- import { isValidAsJSIdentifier } from './util.js';
13
+ import { isURLSpecifier, isValidAsJSIdentifier } from './util.js';
14
14
 
15
- // eslint-disable-next-line max-params, complexity
16
- export function checkCSSModule(
17
- cssModule: CSSModule,
18
- config: CMKConfig,
19
- exportBuilder: ExportBuilder,
20
- matchesPattern: MatchesPattern,
21
- resolver: Resolver,
22
- getCSSModule: (path: string) => CSSModule | undefined,
23
- ): Diagnostic[] {
15
+ export interface CheckerArgs {
16
+ config: CMKConfig;
17
+ getExportRecord: (cssModule: CSSModule) => ExportRecord;
18
+ matchesPattern: MatchesPattern;
19
+ resolver: Resolver;
20
+ getCSSModule: (path: string) => CSSModule | undefined;
21
+ }
22
+
23
+ export function checkCSSModule(cssModule: CSSModule, args: CheckerArgs): Diagnostic[] {
24
+ const { config } = args;
24
25
  const diagnostics: Diagnostic[] = [];
25
26
 
26
27
  for (const token of cssModule.localTokens) {
@@ -37,16 +38,18 @@ export function checkCSSModule(
37
38
  }
38
39
 
39
40
  for (const tokenImporter of cssModule.tokenImporters) {
40
- const from = resolver(tokenImporter.from, { request: cssModule.fileName });
41
- if (!from || !matchesPattern(from)) continue;
42
- const imported = getCSSModule(from);
43
- if (!imported) {
41
+ if (isURLSpecifier(tokenImporter.from)) continue;
42
+ const from = args.resolver(tokenImporter.from, { request: cssModule.fileName });
43
+ if (!from) {
44
44
  diagnostics.push(createCannotImportModuleDiagnostic(cssModule, tokenImporter));
45
45
  continue;
46
46
  }
47
+ if (!args.matchesPattern(from)) continue;
48
+ const imported = args.getCSSModule(from);
49
+ if (!imported) throw new Error('unreachable: `imported` is undefined');
47
50
 
48
51
  if (tokenImporter.type === 'value') {
49
- const exportRecord = exportBuilder.build(imported);
52
+ const exportRecord = args.getExportRecord(imported);
50
53
  for (const value of tokenImporter.values) {
51
54
  if (!exportRecord.allTokens.includes(value.name)) {
52
55
  diagnostics.push(createModuleHasNoExportedTokenDiagnostic(cssModule, tokenImporter, value));
package/src/config.ts CHANGED
@@ -13,6 +13,7 @@ const DEFAULT_INCLUDE_SPEC = '**/*';
13
13
  export interface CMKConfig {
14
14
  includes: string[];
15
15
  excludes: string[];
16
+ enabled: boolean | undefined;
16
17
  dtsOutDir: string;
17
18
  arbitraryExtensions: boolean;
18
19
  namedExports: boolean;
@@ -71,6 +72,7 @@ export interface CMKConfig {
71
72
  interface UnnormalizedRawConfig {
72
73
  includes?: string[];
73
74
  excludes?: string[];
75
+ enabled?: boolean;
74
76
  dtsOutDir?: string;
75
77
  arbitraryExtensions?: boolean;
76
78
  namedExports?: boolean;
@@ -124,6 +126,16 @@ function parseRawData(raw: unknown, tsConfigSourceFile: ts.TsConfigSourceFile):
124
126
  // MEMO: The errors for this option are reported by `tsc` or `tsserver`, so we don't need to report.
125
127
  }
126
128
  if ('cmkOptions' in raw && typeof raw.cmkOptions === 'object' && raw.cmkOptions !== null) {
129
+ if ('enabled' in raw.cmkOptions) {
130
+ if (typeof raw.cmkOptions.enabled === 'boolean') {
131
+ result.config.enabled = raw.cmkOptions.enabled;
132
+ } else {
133
+ result.diagnostics.push({
134
+ category: 'error',
135
+ text: `\`enabled\` in ${tsConfigSourceFile.fileName} must be a boolean.`,
136
+ });
137
+ }
138
+ }
127
139
  if ('dtsOutDir' in raw.cmkOptions) {
128
140
  if (typeof raw.cmkOptions.dtsOutDir === 'string') {
129
141
  result.config.dtsOutDir = raw.cmkOptions.dtsOutDir;
@@ -255,6 +267,7 @@ export function readConfigFile(project: string): CMKConfig {
255
267
  namedExports: parsedTsConfig.config.namedExports ?? false,
256
268
  prioritizeNamedImports: parsedTsConfig.config.prioritizeNamedImports ?? false,
257
269
  keyframes: parsedTsConfig.config.keyframes ?? true,
270
+ enabled: parsedTsConfig.config.enabled,
258
271
  basePath,
259
272
  configFileName,
260
273
  compilerOptions: parsedTsConfig.compilerOptions,
package/src/diagnostic.ts CHANGED
@@ -11,6 +11,8 @@ function convertErrorCategory(category: DiagnosticCategory): ts.DiagnosticCatego
11
11
  switch (category) {
12
12
  case 'error':
13
13
  return ts.DiagnosticCategory.Error;
14
+ case 'warning':
15
+ return ts.DiagnosticCategory.Warning;
14
16
  default:
15
17
  throw new Error(`Unknown category: ${String(category)}`);
16
18
  }
@@ -1,13 +1,8 @@
1
- import type { CSSModule, MatchesPattern, Resolver, Token, TokenImporter } from './type.js';
2
- import { isValidAsJSIdentifier } from './util.js';
1
+ import type { CSSModule, Token, TokenImporter } from './type.js';
2
+ import { isURLSpecifier, isValidAsJSIdentifier } from './util.js';
3
3
 
4
4
  export const STYLES_EXPORT_NAME = 'styles';
5
5
 
6
- export interface GenerateDtsHost {
7
- resolver: Resolver;
8
- matchesPattern: MatchesPattern;
9
- }
10
-
11
6
  export interface GenerateDtsOptions {
12
7
  namedExports: boolean;
13
8
  prioritizeNamedImports: boolean;
@@ -46,11 +41,7 @@ interface GenerateDtsResult {
46
41
  /**
47
42
  * Generate .d.ts from `CSSModule`.
48
43
  */
49
- export function generateDts(
50
- cssModule: CSSModule,
51
- host: GenerateDtsHost,
52
- options: GenerateDtsOptions,
53
- ): GenerateDtsResult {
44
+ export function generateDts(cssModule: CSSModule, options: GenerateDtsOptions): GenerateDtsResult {
54
45
  // Exclude invalid tokens
55
46
  const localTokens = cssModule.localTokens.filter((token) => isValidName(token.name, options));
56
47
  const tokenImporters = cssModule.tokenImporters
@@ -69,10 +60,43 @@ export function generateDts(
69
60
  return tokenImporter;
70
61
  }
71
62
  })
72
- // Exclude token importers for external files
73
63
  .filter((tokenImporter) => {
74
- const resolved = host.resolver(tokenImporter.from, { request: cssModule.fileName });
75
- return resolved !== undefined && host.matchesPattern(resolved);
64
+ /**
65
+ * In principle, token importers with specifiers that cannot be resolved are still included in the type
66
+ * definitions. For example, consider the following:
67
+ *
68
+ * ```css
69
+ * // src/a.module.css
70
+ * @import './unresolved.module.css';
71
+ * @import './unmatched.css';
72
+ * .a_1 { color: red; }
73
+ * ```
74
+ *
75
+ * In this case, CSS Modules Kit generates the following type definitions:
76
+ *
77
+ * ```ts
78
+ * // generated/src/a.module.css.d.ts
79
+ * // @ts-nocheck
80
+ * declare const styles = {
81
+ * a_1: '' as readonly string,
82
+ * ...(await import('./unresolved.module.css')).default,
83
+ * ...(await import('./unmatched.css')).default,
84
+ * };
85
+ * ```
86
+ *
87
+ * Even if `./unresolved.module.css` or `./unmatched.css` does not exist, the same type definitions are
88
+ * generated. It is important that the generated type definitions do not change depending on whether the
89
+ * files exist. This provides the following benefits:
90
+ *
91
+ * - Simplifies the watch mode implementation
92
+ * - Only the type definitions for changed files need to be regenerated
93
+ * - Makes it easier to parallelize code generation
94
+ * - Type definitions can be generated independently per file
95
+ *
96
+ * However, as an exception, URL specifiers are not included in the type definitions, because URL
97
+ * specifiers are typically resolved at runtime.
98
+ */
99
+ return !isURLSpecifier(tokenImporter.from);
76
100
  });
77
101
 
78
102
  if (options.namedExports) {
@@ -82,28 +106,7 @@ export function generateDts(
82
106
  }
83
107
  }
84
108
 
85
- /**
86
- * Generate a d.ts file with named exports.
87
- * @example
88
- * If the CSS module file is:
89
- * ```css
90
- * @import './a.module.css';
91
- * @value local1: string;
92
- * @value imported1, imported2 as aliasedImported2 from './b.module.css';
93
- * .local2 { color: red }
94
- * ```
95
- * The d.ts file would be:
96
- * ```ts
97
- * // @ts-nocheck
98
- * export var local1: string;
99
- * export var local2: string;
100
- * export * from './a.module.css';
101
- * export {
102
- * imported1,
103
- * imported2 as aliasedImported2,
104
- * } from './b.module.css';
105
- * ```
106
- */
109
+ /** Generate a d.ts file with named exports. */
107
110
  function generateNamedExportsDts(
108
111
  localTokens: Token[],
109
112
  tokenImporters: TokenImporter[],
@@ -125,6 +128,25 @@ function generateNamedExportsDts(
125
128
  let text = `// @ts-nocheck\n`;
126
129
 
127
130
  for (const token of localTokens) {
131
+ /**
132
+ * The mapping is created as follows:
133
+ * a.module.css:
134
+ * 1 | .a_1 { color: red; }
135
+ * | ^ mapping.sourceOffsets[0]
136
+ * |
137
+ * 2 | .a_2 { color: blue; }
138
+ * | ^ mapping.sourceOffsets[1]
139
+ * |
140
+ *
141
+ * a.module.css.d.ts:
142
+ * 1 | // @ts-nocheck
143
+ * 2 | export var a_1: string;
144
+ * | ^ mapping.generatedOffsets[0]
145
+ * |
146
+ * 3 | export var a_2: string;
147
+ * | ^ mapping.generatedOffsets[1]
148
+ */
149
+
128
150
  text += `export var `;
129
151
  mapping.sourceOffsets.push(token.loc.start.offset);
130
152
  mapping.generatedOffsets.push(text.length);
@@ -133,12 +155,71 @@ function generateNamedExportsDts(
133
155
  }
134
156
  for (const tokenImporter of tokenImporters) {
135
157
  if (tokenImporter.type === 'import') {
158
+ /**
159
+ * The mapping is created as follows:
160
+ * a.module.css:
161
+ * 1 | @import './b.module.css';
162
+ * | ^ mapping.sourceOffsets[0]
163
+ * |
164
+ * 2 | @import './c.module.css';
165
+ * | ^ mapping.sourceOffsets[1]
166
+ * |
167
+ *
168
+ * a.module.css.d.ts:
169
+ * 1 | // @ts-nocheck
170
+ * 2 | export * from './b.module.css';
171
+ * | ^ mapping.generatedOffsets[0]
172
+ * |
173
+ * 3 | export * from './c.module.css';
174
+ * | ^ mapping.generatedOffsets[1]
175
+ *
176
+ * NOTE: Not only the specifier but also the surrounding quotes are included in the mapping.
177
+ */
178
+
136
179
  text += `export * from `;
137
180
  mapping.sourceOffsets.push(tokenImporter.fromLoc.start.offset - 1);
138
181
  mapping.lengths.push(tokenImporter.from.length + 2);
139
182
  mapping.generatedOffsets.push(text.length);
140
183
  text += `'${tokenImporter.from}';\n`;
141
184
  } else {
185
+ /**
186
+ * The mapping is created as follows:
187
+ * a.module.css:
188
+ * 1 | @value b_1, b_2 from './b.module.css';
189
+ * | ^ ^ ^ mapping.sourceOffsets[2]
190
+ * | ^ ^ mapping.sourceOffsets[1]
191
+ * | ^ mapping.sourceOffsets[0]
192
+ * |
193
+ * 2 | @value c_1 as aliased_c_1 from './c.module.css';
194
+ * | ^ ^ ^ mapping.sourceOffsets[5]
195
+ * | ^ ^ mapping.sourceOffsets[4]
196
+ * | ^ mapping.sourceOffsets[3]
197
+ * |
198
+ *
199
+ * a.module.css.d.ts:
200
+ * 1 | // @ts-nocheck
201
+ * 2 | export {
202
+ * 3 | b_1,
203
+ * | ^ mapping.generatedOffsets[0]
204
+ * |
205
+ * 4 | b_2,
206
+ * | ^ mapping.generatedOffsets[1]
207
+ * |
208
+ * 5 | } from './b.module.css';
209
+ * | ^ mapping.generatedOffsets[2]
210
+ * |
211
+ * 6 | export {
212
+ * 7 | c_1 as aliased_c_1,
213
+ * | ^ ^ mapping.generatedOffsets[4], linkedCodeMapping.sourceOffsets[0]
214
+ * | ^ mapping.generatedOffsets[3], linkedCodeMapping.generatedOffsets[0]
215
+ * |
216
+ * 8 | } from './c.module.css';
217
+ * | ^ mapping.generatedOffsets[5]
218
+ *
219
+ * NOTE: Not only the specifier but also the surrounding quotes are included in the mapping.
220
+ * NOTE: linkedCodeMapping is only generated for tokens that have a `localName` (i.e., aliased tokens).
221
+ */
222
+
142
223
  text += `export {\n`;
143
224
  // eslint-disable-next-line no-loop-func
144
225
  tokenImporter.values.forEach((value) => {
@@ -179,29 +260,7 @@ function generateNamedExportsDts(
179
260
  return { text, mapping, linkedCodeMapping };
180
261
  }
181
262
 
182
- /**
183
- * Generate a d.ts file with a default export.
184
- * @example
185
- * If the CSS module file is:
186
- * ```css
187
- * @import './a.module.css';
188
- * @value local1: string;
189
- * @value imported1, imported2 as aliasedImported2 from './b.module.css';
190
- * .local2 { color: red }
191
- * ```
192
- * The d.ts file would be:
193
- * ```ts
194
- * // @ts-nocheck
195
- * const styles = {
196
- * local1: '' as readonly string,
197
- * local2: '' as readonly string,
198
- * ...(await import('./a.module.css')).default,
199
- * imported1: (await import('./b.module.css')).default.imported1,
200
- * aliasedImported2: (await import('./b.module.css')).default.imported2,
201
- * };
202
- * export default styles;
203
- * ```
204
- */
263
+ /** Generate a d.ts file with a default export. */
205
264
  function generateDefaultExportDts(
206
265
  localTokens: Token[],
207
266
  tokenImporters: TokenImporter[],
@@ -219,9 +278,40 @@ function generateDefaultExportDts(
219
278
  //
220
279
  // If `--skipLibCheck` is false, those errors will be reported by `tsc`. However, these are negligible errors.
221
280
  // Therefore, `@ts-nocheck` is added to the generated type definition file.
222
- let text = `// @ts-nocheck\ndeclare const ${STYLES_EXPORT_NAME} = {\n`;
281
+ let text = `// @ts-nocheck\n`;
223
282
 
283
+ // This is a workaround to avoid the issue described as a "Drawbacks" in https://github.com/mizdra/css-modules-kit/pull/302.
284
+ // It uses the technique from https://stackoverflow.com/a/55541672 to fall back from `any` to `{}`.
285
+ // However, the import type for an unresolvable specifier becomes a special `any` type called `errorType`.
286
+ // The technique from https://stackoverflow.com/a/55541672 does not work with `errorType`.
287
+ // Therefore, this combines it with the approach from https://github.com/microsoft/TypeScript/issues/62972.
288
+ if (tokenImporters.some((importer) => importer.type === 'import')) {
289
+ text += `function blockErrorType<T>(val: T): [0] extends [(1 & T)] ? {} : T;\n`;
290
+ }
291
+
292
+ text += `declare const ${STYLES_EXPORT_NAME} = {\n`;
224
293
  for (const token of localTokens) {
294
+ /**
295
+ * The mapping is created as follows:
296
+ * a.module.css:
297
+ * 1 | .a_1 { color: red; }
298
+ * | ^ mapping.sourceOffsets[0]
299
+ * |
300
+ * 2 | .a_2 { color: blue; }
301
+ * | ^ mapping.sourceOffsets[1]
302
+ * |
303
+ *
304
+ * a.module.css.d.ts:
305
+ * 1 | declare const styles = {
306
+ * 2 | a_1: '' as readonly string,
307
+ * | ^ mapping.generatedOffsets[0]
308
+ * |
309
+ * 3 | a_2: '' as readonly string,
310
+ * | ^ mapping.generatedOffsets[1]
311
+ * |
312
+ * 4 | };
313
+ */
314
+
225
315
  text += ` `;
226
316
  mapping.sourceOffsets.push(token.loc.start.offset);
227
317
  mapping.generatedOffsets.push(text.length);
@@ -230,12 +320,70 @@ function generateDefaultExportDts(
230
320
  }
231
321
  for (const tokenImporter of tokenImporters) {
232
322
  if (tokenImporter.type === 'import') {
233
- text += ` ...(await import(`;
323
+ /**
324
+ * The mapping is created as follows:
325
+ * a.module.css:
326
+ * 1 | @import './b.module.css';
327
+ * | ^ mapping.sourceOffsets[0]
328
+ * |
329
+ * 2 | @import './c.module.css';
330
+ * | ^ mapping.sourceOffsets[1]
331
+ * |
332
+ *
333
+ * a.module.css.d.ts:
334
+ * 1 | declare const styles = {
335
+ * 2 | ...blockErrorType((await import('./b.module.css')).default),
336
+ * | ^ mapping.generatedOffsets[0]
337
+ * |
338
+ * 3 | ...blockErrorType((await import('./c.module.css')).default),
339
+ * | ^ mapping.generatedOffsets[1]
340
+ * |
341
+ * 4 | };
342
+ *
343
+ * NOTE: Not only the specifier but also the surrounding quotes are included in the mapping.
344
+ */
345
+
346
+ text += ` ...blockErrorType((await import(`;
234
347
  mapping.sourceOffsets.push(tokenImporter.fromLoc.start.offset - 1);
235
348
  mapping.lengths.push(tokenImporter.from.length + 2);
236
349
  mapping.generatedOffsets.push(text.length);
237
- text += `'${tokenImporter.from}')).default,\n`;
350
+ text += `'${tokenImporter.from}')).default),\n`;
238
351
  } else {
352
+ /**
353
+ * The mapping is created as follows:
354
+ * a.module.css:
355
+ * 1 | @value b_1, b_2 from './b.module.css';
356
+ * | ^ ^ ^ mapping.sourceOffsets[0]
357
+ * | ^ ^ mapping.sourceOffsets[2]
358
+ * | ^ mapping.sourceOffsets[1]
359
+ * |
360
+ * 2 | @value c_1 as aliased_c_1 from './c.module.css';
361
+ * | ^ ^ ^ mapping.sourceOffsets[4]
362
+ * | ^ ^ mapping.sourceOffsets[3]
363
+ * | ^ mapping.sourceOffsets[5]
364
+ * |
365
+ *
366
+ * a.module.css.d.ts:
367
+ * 1 | declare const styles = {
368
+ * 2 | b_1: (await import('./b.module.css')).default.b_1,
369
+ * | ^ ^ ^ linkedCodeMapping.generatedOffsets[0]
370
+ * | ^ ^ mapping.generatedOffsets[1]
371
+ * | ^ mapping.generatedOffsets[0], linkedCodeMapping.sourceOffsets[0]
372
+ * |
373
+ * 3 | b_2: (await import('./b.module.css')).default.b_2,
374
+ * | ^ ^ linkedCodeMapping.generatedOffsets[1]
375
+ * | ^ mapping.generatedOffsets[2], linkedCodeMapping.sourceOffsets[1]
376
+ * |
377
+ * 4 | aliased_c_1: (await import('./c.module.css')).default.c_1,
378
+ * | ^ ^ ^ mapping.generatedOffsets[5], linkedCodeMapping.generatedOffsets[2]
379
+ * | ^ ^ mapping.generatedOffsets[4]
380
+ * | ^ mapping.generatedOffsets[3], linkedCodeMapping.sourceOffsets[2]
381
+ * |
382
+ * 5 | };
383
+ *
384
+ * NOTE: Not only the specifier but also the surrounding quotes are included in the mapping.
385
+ */
386
+
239
387
  // eslint-disable-next-line no-loop-func
240
388
  tokenImporter.values.forEach((value, i) => {
241
389
  const localName = value.localName ?? value.name;
@@ -254,9 +402,11 @@ function generateDefaultExportDts(
254
402
  mapping.generatedOffsets.push(text.length);
255
403
  }
256
404
  text += `'${tokenImporter.from}')).default.`;
257
- mapping.sourceOffsets.push(value.loc.start.offset);
258
- mapping.lengths.push(value.name.length);
259
- mapping.generatedOffsets.push(text.length);
405
+ if ('localName' in value) {
406
+ mapping.sourceOffsets.push(value.loc.start.offset);
407
+ mapping.lengths.push(value.name.length);
408
+ mapping.generatedOffsets.push(text.length);
409
+ }
260
410
  linkedCodeMapping.generatedOffsets.push(text.length);
261
411
  linkedCodeMapping.generatedLengths.push(value.name.length);
262
412
  text += `${value.name},\n`;
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export {
15
15
  type Resolver,
16
16
  type MatchesPattern,
17
17
  type ExportBuilder,
18
+ type ExportRecord,
18
19
  type DiagnosticSourceFile,
19
20
  type Diagnostic,
20
21
  type DiagnosticWithLocation,
@@ -33,8 +34,8 @@ export {
33
34
  createMatchesPattern,
34
35
  getFileNamesByPattern,
35
36
  } from './file.js';
36
- export { checkCSSModule } from './checker.js';
37
+ export { checkCSSModule, type CheckerArgs } from './checker.js';
37
38
  export { createExportBuilder } from './export-builder.js';
38
- export { join, resolve, relative, dirname, basename, parse, isAbsolute } from './path.js';
39
+ export { join, resolve, relative, dirname, basename, parse } from './path.js';
39
40
  export { findUsedTokenNames } from './util.js';
40
41
  export { convertDiagnostic, convertDiagnosticWithLocation, convertSystemError } from './diagnostic.js';
package/src/path.ts CHANGED
@@ -32,6 +32,3 @@ export function parse(path: string): ParsedPath {
32
32
  const { root, dir, base, name, ext } = nodePath.parse(path);
33
33
  return { root: slash(root), dir: slash(dir), base, name, ext };
34
34
  }
35
-
36
- // eslint-disable-next-line @typescript-eslint/unbound-method
37
- export const isAbsolute = nodePath.isAbsolute;
package/src/resolver.ts CHANGED
@@ -1,21 +1,19 @@
1
- import { fileURLToPath, pathToFileURL } from 'node:url';
2
1
  import type { CompilerOptions } from 'typescript';
3
2
  import ts from 'typescript';
4
- import { isAbsolute, resolve } from './path.js';
5
3
  import type { Resolver, ResolverOptions } from './type.js';
4
+ import { isURLSpecifier } from './util.js';
6
5
 
7
6
  export function createResolver(
8
7
  compilerOptions: CompilerOptions,
9
8
  moduleResolutionCache: ts.ModuleResolutionCache | undefined,
10
9
  ): Resolver {
11
- return (_specifier: string, options: ResolverOptions) => {
12
- let specifier = _specifier;
13
-
10
+ return (specifier: string, options: ResolverOptions) => {
11
+ if (isURLSpecifier(specifier)) return undefined;
14
12
  const host: ts.ModuleResolutionHost = {
15
13
  ...ts.sys,
16
14
  fileExists: (fileName) => {
17
- if (fileName.endsWith('.module.d.css.ts')) {
18
- return ts.sys.fileExists(fileName.replace(/\.module\.d\.css\.ts$/u, '.module.css'));
15
+ if (fileName.endsWith('.d.css.ts')) {
16
+ return ts.sys.fileExists(fileName.replace(/\.d\.css\.ts$/u, '.css'));
19
17
  }
20
18
  return ts.sys.fileExists(fileName);
21
19
  },
@@ -28,28 +26,8 @@ export function createResolver(
28
26
  moduleResolutionCache,
29
27
  );
30
28
  if (resolvedModule) {
31
- // TODO: Logging that the paths is used.
32
- specifier = resolvedModule.resolvedFileName.replace(/\.module\.d\.css\.ts$/u, '.module.css');
33
- }
34
- if (isAbsolute(specifier)) {
35
- return resolve(specifier);
36
- } else if (isRelativeSpecifier(specifier)) {
37
- // Convert the specifier to an absolute path
38
- // NOTE: Node.js resolves relative specifier with standard relative URL resolution semantics. So we will follow that here as well.
39
- // ref: https://nodejs.org/docs/latest-v23.x/api/esm.html#terminology
40
- return resolve(fileURLToPath(new URL(specifier, pathToFileURL(options.request)).href));
41
- } else {
42
- // Do not support URL or bare specifiers
43
- // TODO: Logging that the specifier could not resolve.
44
- return undefined;
29
+ return resolvedModule.resolvedFileName.replace(/\.d\.css\.ts$/u, '.css');
45
30
  }
31
+ return undefined;
46
32
  };
47
33
  }
48
-
49
- /**
50
- * Check if the specifier is a relative specifier.
51
- * @see https://nodejs.org/docs/latest-v23.x/api/esm.html#terminology
52
- */
53
- function isRelativeSpecifier(specifier: string): boolean {
54
- return specifier.startsWith('./') || specifier.startsWith('../');
55
- }
package/src/type.ts CHANGED
@@ -151,7 +151,7 @@ export interface ExportBuilder {
151
151
  clearCache(): void;
152
152
  }
153
153
 
154
- export type DiagnosticCategory = 'error';
154
+ export type DiagnosticCategory = 'error' | 'warning';
155
155
 
156
156
  export interface DiagnosticSourceFile {
157
157
  fileName: string;
@@ -5,7 +5,7 @@ declare module 'typescript' {
5
5
  readonly files: readonly string[];
6
6
  readonly directories: readonly string[];
7
7
  }
8
- // eslint-disable-next-line max-params
8
+
9
9
  export function matchFiles(
10
10
  path: string,
11
11
  extensions: readonly string[] | undefined,
package/src/util.ts CHANGED
@@ -25,3 +25,7 @@ export function findUsedTokenNames(componentText: string): Set<string> {
25
25
  }
26
26
  return usedClassNames;
27
27
  }
28
+
29
+ export function isURLSpecifier(specifier: string): boolean {
30
+ return URL.canParse(specifier);
31
+ }