@css-modules-kit/core 0.1.0 → 0.3.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.
Files changed (72) hide show
  1. package/dist/checker.d.ts +2 -6
  2. package/dist/checker.d.ts.map +1 -1
  3. package/dist/checker.js +4 -6
  4. package/dist/checker.js.map +1 -1
  5. package/dist/config.d.ts +13 -17
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +41 -30
  8. package/dist/config.js.map +1 -1
  9. package/dist/diagnostic.d.ts +6 -33
  10. package/dist/diagnostic.d.ts.map +1 -1
  11. package/dist/diagnostic.js +71 -0
  12. package/dist/diagnostic.js.map +1 -1
  13. package/dist/dts-creator.d.ts +8 -28
  14. package/dist/dts-creator.d.ts.map +1 -1
  15. package/dist/dts-creator.js +106 -7
  16. package/dist/dts-creator.js.map +1 -1
  17. package/dist/export-builder.d.ts +1 -14
  18. package/dist/export-builder.d.ts.map +1 -1
  19. package/dist/export-builder.js.map +1 -1
  20. package/dist/file.d.ts +1 -1
  21. package/dist/file.d.ts.map +1 -1
  22. package/dist/file.js.map +1 -1
  23. package/dist/index.d.ts +7 -7
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +5 -2
  26. package/dist/index.js.map +1 -1
  27. package/dist/parser/at-import-parser.d.ts +1 -1
  28. package/dist/parser/at-import-parser.d.ts.map +1 -1
  29. package/dist/parser/at-value-parser.d.ts +2 -3
  30. package/dist/parser/at-value-parser.d.ts.map +1 -1
  31. package/dist/parser/at-value-parser.js +2 -13
  32. package/dist/parser/at-value-parser.js.map +1 -1
  33. package/dist/parser/css-module-parser.d.ts +2 -83
  34. package/dist/parser/css-module-parser.d.ts.map +1 -1
  35. package/dist/parser/css-module-parser.js +7 -8
  36. package/dist/parser/css-module-parser.js.map +1 -1
  37. package/dist/parser/rule-parser.d.ts +8 -4
  38. package/dist/parser/rule-parser.d.ts.map +1 -1
  39. package/dist/parser/rule-parser.js +9 -10
  40. package/dist/parser/rule-parser.js.map +1 -1
  41. package/dist/path.d.ts +0 -1
  42. package/dist/path.d.ts.map +1 -1
  43. package/dist/path.js +1 -3
  44. package/dist/path.js.map +1 -1
  45. package/dist/resolver.d.ts +3 -12
  46. package/dist/resolver.d.ts.map +1 -1
  47. package/dist/resolver.js +2 -2
  48. package/dist/resolver.js.map +1 -1
  49. package/dist/type.d.ts +174 -0
  50. package/dist/type.d.ts.map +1 -0
  51. package/dist/type.js +3 -0
  52. package/dist/type.js.map +1 -0
  53. package/package.json +2 -2
  54. package/src/checker.ts +13 -15
  55. package/src/config.ts +50 -45
  56. package/src/diagnostic.ts +69 -29
  57. package/src/dts-creator.ts +120 -13
  58. package/src/export-builder.ts +1 -16
  59. package/src/file.ts +1 -2
  60. package/src/index.ts +14 -14
  61. package/src/parser/at-import-parser.ts +1 -1
  62. package/src/parser/at-value-parser.ts +5 -17
  63. package/src/parser/css-module-parser.ts +16 -98
  64. package/src/parser/rule-parser.ts +17 -14
  65. package/src/path.ts +0 -3
  66. package/src/resolver.ts +12 -15
  67. package/src/type.ts +191 -0
  68. package/dist/parser/location.d.ts +0 -34
  69. package/dist/parser/location.d.ts.map +0 -1
  70. package/dist/parser/location.js +0 -9
  71. package/dist/parser/location.js.map +0 -1
  72. package/src/parser/location.ts +0 -40
@@ -1,12 +1,11 @@
1
- import type { MatchesPattern } from './file.js';
2
- import type { CSSModule } from './parser/css-module-parser.js';
3
- import type { Resolver } from './resolver.js';
1
+ import type { CSSModule, MatchesPattern, Resolver, Token, TokenImporter } from './type.js';
4
2
 
5
3
  export const STYLES_EXPORT_NAME = 'styles';
6
4
 
7
5
  export interface CreateDtsOptions {
8
6
  resolver: Resolver;
9
7
  matchesPattern: MatchesPattern;
8
+ namedExports: boolean;
10
9
  }
11
10
 
12
11
  interface CodeMapping {
@@ -31,8 +30,122 @@ interface LinkedCodeMapping extends CodeMapping {
31
30
  generatedLengths: number[];
32
31
  }
33
32
 
33
+ interface CreateDtsResult {
34
+ text: string;
35
+ mapping: CodeMapping;
36
+ linkedCodeMapping: LinkedCodeMapping;
37
+ }
38
+
39
+ /**
40
+ * Create a d.ts file.
41
+ */
42
+ export function createDts(cssModules: CSSModule, options: CreateDtsOptions): CreateDtsResult {
43
+ // Filter external files
44
+ const tokenImporters = cssModules.tokenImporters.filter((tokenImporter) => {
45
+ const resolved = options.resolver(tokenImporter.from, { request: cssModules.fileName });
46
+ return resolved !== undefined && options.matchesPattern(resolved);
47
+ });
48
+ if (options.namedExports) {
49
+ return createNamedExportsDts(cssModules.localTokens, tokenImporters);
50
+ } else {
51
+ return createDefaultExportDts(cssModules.localTokens, tokenImporters);
52
+ }
53
+ }
54
+
34
55
  /**
35
- * Create a d.ts file from a CSS module file.
56
+ * Create a d.ts file with named exports.
57
+ * @example
58
+ * If the CSS module file is:
59
+ * ```css
60
+ * @import './a.module.css';
61
+ * @value local1: string;
62
+ * @value imported1, imported2 as aliasedImported2 from './b.module.css';
63
+ * .local2 { color: red }
64
+ * ```
65
+ * The d.ts file would be:
66
+ * ```ts
67
+ * // @ts-nocheck
68
+ * export var local1: string;
69
+ * export var local2: string;
70
+ * export * from './a.module.css';
71
+ * export {
72
+ * imported1,
73
+ * imported2 as aliasedImported2,
74
+ * } from './b.module.css';
75
+ * ```
76
+ */
77
+ function createNamedExportsDts(
78
+ localTokens: Token[],
79
+ tokenImporters: TokenImporter[],
80
+ ): { text: string; mapping: CodeMapping; linkedCodeMapping: LinkedCodeMapping } {
81
+ const mapping: CodeMapping = { sourceOffsets: [], lengths: [], generatedOffsets: [] };
82
+ const linkedCodeMapping: LinkedCodeMapping = {
83
+ sourceOffsets: [],
84
+ lengths: [],
85
+ generatedOffsets: [],
86
+ generatedLengths: [],
87
+ };
88
+
89
+ // MEMO: Depending on the TypeScript compilation options, the generated type definition file contains compile errors.
90
+ // For example, it contains `Top-level 'await' expressions are only allowed when the 'module' option is set to ...` error.
91
+ //
92
+ // If `--skipLibCheck` is false, those errors will be reported by `tsc`. However, these are negligible errors.
93
+ // Therefore, `@ts-nocheck` is added to the generated type definition file.
94
+ let text = `// @ts-nocheck\n`;
95
+
96
+ for (const token of localTokens) {
97
+ text += `export var `;
98
+ mapping.sourceOffsets.push(token.loc.start.offset);
99
+ mapping.generatedOffsets.push(text.length);
100
+ mapping.lengths.push(token.name.length);
101
+ text += `${token.name}: string;\n`;
102
+ }
103
+ for (const tokenImporter of tokenImporters) {
104
+ if (tokenImporter.type === 'import') {
105
+ text += `export * from `;
106
+ mapping.sourceOffsets.push(tokenImporter.fromLoc.start.offset - 1);
107
+ mapping.lengths.push(tokenImporter.from.length + 2);
108
+ mapping.generatedOffsets.push(text.length);
109
+ text += `'${tokenImporter.from}';\n`;
110
+ } else {
111
+ text += `export {\n`;
112
+ // eslint-disable-next-line no-loop-func
113
+ tokenImporter.values.forEach((value) => {
114
+ const localName = value.localName ?? value.name;
115
+ const localLoc = value.localLoc ?? value.loc;
116
+ text += ` `;
117
+ if ('localName' in value) {
118
+ mapping.sourceOffsets.push(value.loc.start.offset);
119
+ mapping.lengths.push(value.name.length);
120
+ mapping.generatedOffsets.push(text.length);
121
+ linkedCodeMapping.generatedOffsets.push(text.length);
122
+ linkedCodeMapping.generatedLengths.push(value.name.length);
123
+ text += `${value.name} as `;
124
+ mapping.sourceOffsets.push(localLoc.start.offset);
125
+ mapping.lengths.push(localName.length);
126
+ mapping.generatedOffsets.push(text.length);
127
+ linkedCodeMapping.sourceOffsets.push(text.length);
128
+ linkedCodeMapping.lengths.push(localName.length);
129
+ text += `${localName},\n`;
130
+ } else {
131
+ mapping.sourceOffsets.push(value.loc.start.offset);
132
+ mapping.lengths.push(value.name.length);
133
+ mapping.generatedOffsets.push(text.length);
134
+ text += `${value.name},\n`;
135
+ }
136
+ });
137
+ text += `} from `;
138
+ mapping.sourceOffsets.push(tokenImporter.fromLoc.start.offset - 1);
139
+ mapping.lengths.push(tokenImporter.from.length + 2);
140
+ mapping.generatedOffsets.push(text.length);
141
+ text += `'${tokenImporter.from}';\n`;
142
+ }
143
+ }
144
+ return { text, mapping, linkedCodeMapping };
145
+ }
146
+
147
+ /**
148
+ * Create a d.ts file with a default export.
36
149
  * @example
37
150
  * If the CSS module file is:
38
151
  * ```css
@@ -54,9 +167,9 @@ interface LinkedCodeMapping extends CodeMapping {
54
167
  * export default styles;
55
168
  * ```
56
169
  */
57
- export function createDts(
58
- { fileName, localTokens, tokenImporters: _tokenImporters }: CSSModule,
59
- options: CreateDtsOptions,
170
+ function createDefaultExportDts(
171
+ localTokens: Token[],
172
+ tokenImporters: TokenImporter[],
60
173
  ): { text: string; mapping: CodeMapping; linkedCodeMapping: LinkedCodeMapping } {
61
174
  const mapping: CodeMapping = { sourceOffsets: [], lengths: [], generatedOffsets: [] };
62
175
  const linkedCodeMapping: LinkedCodeMapping = {
@@ -66,12 +179,6 @@ export function createDts(
66
179
  generatedLengths: [],
67
180
  };
68
181
 
69
- // Filter external files
70
- const tokenImporters = _tokenImporters.filter((tokenImporter) => {
71
- const resolved = options.resolver(tokenImporter.from, { request: fileName });
72
- return resolved !== undefined && options.matchesPattern(resolved);
73
- });
74
-
75
182
  // MEMO: Depending on the TypeScript compilation options, the generated type definition file contains compile errors.
76
183
  // For example, it contains `Top-level 'await' expressions are only allowed when the 'module' option is set to ...` error.
77
184
  //
@@ -1,6 +1,4 @@
1
- import type { MatchesPattern } from './file.js';
2
- import type { CSSModule } from './parser/css-module-parser.js';
3
- import type { Resolver } from './resolver.js';
1
+ import type { CSSModule, ExportBuilder, ExportRecord, MatchesPattern, Resolver } from './type.js';
4
2
 
5
3
  export interface ExportBuilderHost {
6
4
  matchesPattern: MatchesPattern;
@@ -8,19 +6,6 @@ export interface ExportBuilderHost {
8
6
  resolver: Resolver;
9
7
  }
10
8
 
11
- /**
12
- * The export token record of a CSS module.
13
- */
14
- export interface ExportRecord {
15
- /** The all exported tokens of the CSS module. */
16
- allTokens: string[];
17
- }
18
-
19
- export interface ExportBuilder {
20
- build(cssModule: CSSModule): ExportRecord;
21
- clearCache(): void;
22
- }
23
-
24
9
  /**
25
10
  * A builder for exported token records of CSS modules.
26
11
  */
package/src/file.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import ts from 'typescript';
2
2
  import { join, parse } from './path.js';
3
+ import type { MatchesPattern } from './type.js';
3
4
 
4
5
  export const CSS_MODULE_EXTENSION = '.module.css';
5
6
  const COMPONENT_EXTENSIONS = ['.tsx', '.jsx'];
@@ -53,8 +54,6 @@ export function findComponentFileSync(
53
54
  return undefined;
54
55
  }
55
56
 
56
- export type MatchesPattern = (fileName: string) => boolean;
57
-
58
57
  /**
59
58
  * Create a function that checks whether the given file name matches the pattern.
60
59
  * This does not access the file system.
package/src/index.ts CHANGED
@@ -1,28 +1,28 @@
1
1
  export type { CMKConfig } from './config.js';
2
2
  export { readConfigFile } from './config.js';
3
3
  export { TsConfigFileNotFoundError, SystemError } from './error.js';
4
+ export { parseCSSModule, type ParseCSSModuleOptions, type ParseCSSModuleResult } from './parser/css-module-parser.js';
5
+ export { parseRule } from './parser/rule-parser.js';
4
6
  export {
5
- parseCSSModule,
6
- type ParseCSSModuleOptions,
7
+ type Location,
8
+ type Position,
7
9
  type CSSModule,
8
10
  type Token,
9
11
  type AtImportTokenImporter,
10
12
  type TokenImporter,
11
13
  type AtValueTokenImporter,
12
14
  type AtValueTokenImporterValue,
13
- type ParseCSSModuleResult,
14
- } from './parser/css-module-parser.js';
15
- export { type Location, type Position } from './parser/location.js';
16
- export { parseRule } from './parser/rule-parser.js';
17
- export {
15
+ type Resolver,
16
+ type MatchesPattern,
17
+ type ExportBuilder,
18
+ type DiagnosticSourceFile,
18
19
  type Diagnostic,
19
- type SemanticDiagnostic,
20
- type SyntacticDiagnostic,
20
+ type DiagnosticWithLocation,
21
21
  type DiagnosticCategory,
22
22
  type DiagnosticPosition,
23
- } from './diagnostic.js';
23
+ } from './type.js';
24
24
  export { type CreateDtsOptions, createDts, STYLES_EXPORT_NAME } from './dts-creator.js';
25
- export { createResolver, type Resolver } from './resolver.js';
25
+ export { createResolver } from './resolver.js';
26
26
  export {
27
27
  CSS_MODULE_EXTENSION,
28
28
  getCssModuleFileName,
@@ -30,11 +30,11 @@ export {
30
30
  isCSSModuleFile,
31
31
  findComponentFile,
32
32
  findComponentFileSync,
33
- type MatchesPattern,
34
33
  createMatchesPattern,
35
34
  getFileNamesByPattern,
36
35
  } from './file.js';
37
36
  export { checkCSSModule } from './checker.js';
38
- export { type ExportBuilder, createExportBuilder } from './export-builder.js';
39
- export { join, resolve, relative, dirname, basename, parse, matchesGlob, isAbsolute } from './path.js';
37
+ export { createExportBuilder } from './export-builder.js';
38
+ export { join, resolve, relative, dirname, basename, parse, isAbsolute } from './path.js';
40
39
  export { findUsedTokenNames } from './util.js';
40
+ export { convertDiagnostic, convertDiagnosticWithLocation, convertSystemError } from './diagnostic.js';
@@ -1,6 +1,6 @@
1
1
  import type { AtRule } from 'postcss';
2
2
  import postcssValueParser from 'postcss-value-parser';
3
- import type { Location } from './location.js';
3
+ import type { Location } from '../type.js';
4
4
 
5
5
  interface ParsedAtImport {
6
6
  from: string;
@@ -1,6 +1,5 @@
1
1
  import type { AtRule } from 'postcss';
2
- import type { DiagnosticPosition, SyntacticDiagnostic } from '../diagnostic.js';
3
- import type { Location } from './location.js';
2
+ import type { DiagnosticPosition, DiagnosticWithDetachedLocation, Location } from '../type.js';
4
3
 
5
4
  interface ValueDeclaration {
6
5
  type: 'valueDeclaration';
@@ -25,7 +24,7 @@ type ParsedAtValue = ValueDeclaration | ValueImportDeclaration;
25
24
 
26
25
  interface ParseAtValueResult {
27
26
  atValue?: ParsedAtValue;
28
- diagnostics: SyntacticDiagnostic[];
27
+ diagnostics: DiagnosticWithDetachedLocation[];
29
28
  }
30
29
 
31
30
  const VALUE_IMPORT_PATTERN = /^(.+?)\s+from\s+("[^"]*"|'[^']*')$/du;
@@ -53,7 +52,7 @@ const IMPORTED_ITEM_PATTERN = /^([\w-]+)(?:\s+as\s+([\w-]+))?/du;
53
52
  // MEMO: css-modules-kit does not support `@value` with variable module name (e.g., `@value a from moduleName;`) to simplify the implementation.
54
53
  export function parseAtValue(atValue: AtRule): ParseAtValueResult {
55
54
  const matchesForValueImport = atValue.params.match(VALUE_IMPORT_PATTERN);
56
- const diagnostics: SyntacticDiagnostic[] = [];
55
+ const diagnostics: DiagnosticWithDetachedLocation[] = [];
57
56
  if (matchesForValueImport) {
58
57
  const [, importedItems, from] = matchesForValueImport as [string, string, string];
59
58
  // The length of the `@value ` part in `@value import1 from '...'`
@@ -101,15 +100,9 @@ export function parseAtValue(atValue: AtRule): ParseAtValueResult {
101
100
  line: atValue.source!.start!.line,
102
101
  column: atValue.source!.start!.column + baseLength + currentItemIndex,
103
102
  };
104
- const end: DiagnosticPosition = {
105
- line: start.line,
106
- column: start.column + alias.length,
107
- };
108
103
  diagnostics.push({
109
- type: 'syntactic',
110
- fileName: atValue.source!.input.file!,
111
104
  start,
112
- end,
105
+ length: alias.length,
113
106
  text: `\`${alias}\` is invalid syntax.`,
114
107
  category: 'error',
115
108
  });
@@ -160,16 +153,11 @@ export function parseAtValue(atValue: AtRule): ParseAtValueResult {
160
153
  return { atValue: parsedAtValue, diagnostics };
161
154
  }
162
155
  diagnostics.push({
163
- type: 'syntactic',
164
- fileName: atValue.source!.input.file!,
165
156
  start: {
166
157
  line: atValue.source!.start!.line,
167
158
  column: atValue.source!.start!.column,
168
159
  },
169
- end: {
170
- line: atValue.source!.end!.line,
171
- column: atValue.source!.end!.column + 1,
172
- },
160
+ length: atValue.source!.end!.offset - atValue.source!.start!.offset,
173
161
  text: `\`${atValue.toString()}\` is a invalid syntax.`,
174
162
  category: 'error',
175
163
  });
@@ -1,10 +1,15 @@
1
1
  import type { AtRule, Node, Root, Rule } from 'postcss';
2
2
  import { CssSyntaxError, parse } from 'postcss';
3
3
  import safeParser from 'postcss-safe-parser';
4
- import type { SyntacticDiagnostic } from '../diagnostic.js';
4
+ import type {
5
+ CSSModule,
6
+ DiagnosticWithDetachedLocation,
7
+ DiagnosticWithLocation,
8
+ Token,
9
+ TokenImporter,
10
+ } from '../type.js';
5
11
  import { parseAtImport } from './at-import-parser.js';
6
12
  import { parseAtValue } from './at-value-parser.js';
7
- import { type Location } from './location.js';
8
13
  import { parseRule } from './rule-parser.js';
9
14
 
10
15
  type AtImport = AtRule & { name: 'import' };
@@ -30,7 +35,7 @@ function isRuleNode(node: Node): node is Rule {
30
35
  * Collect tokens from the AST.
31
36
  */
32
37
  function collectTokens(ast: Root) {
33
- const allDiagnostics: SyntacticDiagnostic[] = [];
38
+ const allDiagnostics: DiagnosticWithDetachedLocation[] = [];
34
39
  const localTokens: Token[] = [];
35
40
  const tokenImporters: TokenImporter[] = [];
36
41
  ast.walk((node) => {
@@ -59,92 +64,6 @@ function collectTokens(ast: Root) {
59
64
  return { localTokens, tokenImporters, diagnostics: allDiagnostics };
60
65
  }
61
66
 
62
- /** The item being exported from a CSS module file (a.k.a. token). */
63
- export interface Token {
64
- /** The token name. */
65
- name: string;
66
- /** The location of the token in the source file. */
67
- loc: Location;
68
- }
69
-
70
- /**
71
- * A token importer using `@import '...'`.
72
- * `@import` imports all tokens from the file. Therefore, it does not have
73
- * the name of the imported token unlike {@link AtValueTokenImporter}.
74
- */
75
- export interface AtImportTokenImporter {
76
- type: 'import';
77
- /**
78
- * The specifier of the file from which the token is imported.
79
- * This is a string before being resolved and unquoted.
80
- * @example `@import './a.module.css'` would have `from` as `'./a.module.css'`.
81
- */
82
- from: string;
83
- /** The location of the `from` in *.module.css file. */
84
- fromLoc: Location;
85
- }
86
-
87
- /** A token importer using `@value ... from '...'`. */
88
- export interface AtValueTokenImporter {
89
- type: 'value';
90
- /** The values imported from the file. */
91
- values: AtValueTokenImporterValue[];
92
- /**
93
- * The specifier of the file from which the token is imported.
94
- * This is a string before being resolved and unquoted.
95
- * @example `@value a from './a.module.css'` would have `from` as `'./a.module.css'`.
96
- */
97
- from: string;
98
- /** The location of the `from` in *.module.css file. */
99
- fromLoc: Location;
100
- }
101
-
102
- /** A value imported from a CSS module file using `@value ... from '...'`. */
103
- export interface AtValueTokenImporterValue {
104
- /**
105
- * The name of the token in the file from which it is imported.
106
- * @example `@value a from './a.module.css'` would have `name` as `'a'`.
107
- * @example `@value a as b from './a.module.css'` would have `name` as `'a'`.
108
- */
109
- name: string;
110
- /** The location of the `name` in *.module.css file. */
111
- loc: Location;
112
- /**
113
- * The name of the token in the current file.
114
- * @example `@value a from './a.module.css'` would not have `localName`.
115
- * @example `@value a as b from './a.module.css'` would have `localName` as `'b'`.
116
- */
117
- localName?: string;
118
- /**
119
- * The location of the `localName` in *.module.css file.
120
- * This is `undefined` when `localName` is `undefined`.
121
- */
122
- localLoc?: Location;
123
- }
124
-
125
- export type TokenImporter = AtImportTokenImporter | AtValueTokenImporter;
126
-
127
- export interface CSSModule {
128
- /** Absolute path of the file */
129
- fileName: string;
130
- /**
131
- * List of token names defined in the file.
132
- * @example
133
- * Consider the following file:
134
- * ```css
135
- * .foo { color: red }
136
- * .bar, .baz { color: red }
137
- * ```
138
- * The `localTokens` for this file would be `['foo', 'bar', 'baz']`.
139
- */
140
- localTokens: Token[];
141
- /**
142
- * List of token importers in the file.
143
- * Token importer is a statement that imports tokens from another file.
144
- */
145
- tokenImporters: TokenImporter[];
146
- }
147
-
148
67
  export interface ParseCSSModuleOptions {
149
68
  fileName: string;
150
69
  safe: boolean;
@@ -152,11 +71,12 @@ export interface ParseCSSModuleOptions {
152
71
 
153
72
  export interface ParseCSSModuleResult {
154
73
  cssModule: CSSModule;
155
- diagnostics: SyntacticDiagnostic[];
74
+ diagnostics: DiagnosticWithLocation[];
156
75
  }
157
76
 
158
77
  export function parseCSSModule(text: string, { fileName, safe }: ParseCSSModuleOptions): ParseCSSModuleResult {
159
78
  let ast: Root;
79
+ const diagnosticSourceFile = { fileName, text };
160
80
  try {
161
81
  const parser = safe ? safeParser : parse;
162
82
  ast = parser(text, { from: fileName });
@@ -164,16 +84,13 @@ export function parseCSSModule(text: string, { fileName, safe }: ParseCSSModuleO
164
84
  if (e instanceof CssSyntaxError) {
165
85
  const start = { line: e.line ?? 1, column: e.column ?? 1 };
166
86
  return {
167
- cssModule: { fileName, localTokens: [], tokenImporters: [] },
87
+ cssModule: { fileName, text, localTokens: [], tokenImporters: [] },
168
88
  diagnostics: [
169
89
  {
170
- type: 'syntactic',
171
- fileName,
90
+ file: diagnosticSourceFile,
172
91
  start,
173
- ...(e.endLine !== undefined &&
174
- e.endColumn !== undefined && {
175
- end: { line: e.endLine, column: e.endColumn },
176
- }),
92
+ // TODO: Assign correct length (e.g. `e.endOffset - e.offset`)
93
+ length: 1,
177
94
  text: e.reason,
178
95
  category: 'error',
179
96
  },
@@ -185,8 +102,9 @@ export function parseCSSModule(text: string, { fileName, safe }: ParseCSSModuleO
185
102
  const { localTokens, tokenImporters, diagnostics } = collectTokens(ast);
186
103
  const cssModule = {
187
104
  fileName,
105
+ text,
188
106
  localTokens,
189
107
  tokenImporters,
190
108
  };
191
- return { cssModule, diagnostics };
109
+ return { cssModule, diagnostics: diagnostics.map((diagnostic) => ({ ...diagnostic, file: diagnosticSourceFile })) };
192
110
  }
@@ -1,16 +1,25 @@
1
1
  import type { Rule } from 'postcss';
2
2
  import selectorParser from 'postcss-selector-parser';
3
- import type { SyntacticDiagnostic } from '../diagnostic.js';
4
- import { calcDiagnosticsLocationForSelectorParserNode, type Location } from './location.js';
3
+ import type { DiagnosticPosition, DiagnosticWithDetachedLocation, Location } from '../type.js';
4
+
5
+ function calcDiagnosticsLocationForSelectorParserNode(
6
+ rule: Rule,
7
+ node: selectorParser.Node,
8
+ ): { start: DiagnosticPosition; length: number } {
9
+ const start = rule.positionBy({ index: node.sourceIndex });
10
+ const length = node.toString().length;
11
+ return { start, length };
12
+ }
13
+ export { calcDiagnosticsLocationForSelectorParserNode as calcDiagnosticsLocationForSelectorParserNodeForTest };
5
14
 
6
15
  interface CollectResult {
7
16
  classNames: selectorParser.ClassName[];
8
- diagnostics: SyntacticDiagnostic[];
17
+ diagnostics: DiagnosticWithDetachedLocation[];
9
18
  }
10
19
 
11
20
  function flatCollectResults(results: CollectResult[]): CollectResult {
12
21
  const classNames: selectorParser.ClassName[] = [];
13
- const diagnostics: SyntacticDiagnostic[] = [];
22
+ const diagnostics: DiagnosticWithDetachedLocation[] = [];
14
23
  for (const result of results) {
15
24
  classNames.push(...result.classNames);
16
25
  diagnostics.push(...result.diagnostics);
@@ -25,9 +34,7 @@ function convertClassNameToCollectResult(rule: Rule, node: selectorParser.ClassN
25
34
  const name = (node as any).raws?.value ?? node.value;
26
35
 
27
36
  if (!JS_IDENTIFIER_PATTERN.test(name)) {
28
- const diagnostic: SyntacticDiagnostic = {
29
- type: 'syntactic',
30
- fileName: rule.source!.input.file!,
37
+ const diagnostic: DiagnosticWithDetachedLocation = {
31
38
  ...calcDiagnosticsLocationForSelectorParserNode(rule, node),
32
39
  text: `\`${name}\` is not allowed because it is not a valid JavaScript identifier.`,
33
40
  category: 'error',
@@ -67,9 +74,7 @@ function collectLocalClassNames(rule: Rule, root: selectorParser.Root): CollectR
67
74
  if (node.nodes.length === 0) {
68
75
  // `node` is `:local` or `:global` (without any arguments)
69
76
  // We don't support `:local` and `:global` (without any arguments) because they are complex.
70
- const diagnostic: SyntacticDiagnostic = {
71
- type: 'syntactic',
72
- fileName: rule.source!.input.file!,
77
+ const diagnostic: DiagnosticWithDetachedLocation = {
73
78
  ...calcDiagnosticsLocationForSelectorParserNode(rule, node),
74
79
  text: `\`${node.value}\` is not supported. Use \`${node.value}(...)\` instead.`,
75
80
  category: 'error',
@@ -78,9 +83,7 @@ function collectLocalClassNames(rule: Rule, root: selectorParser.Root): CollectR
78
83
  } else {
79
84
  // `node` is `:local(...)` or `:global(...)` (with arguments)
80
85
  if (wrappedBy !== undefined) {
81
- const diagnostic: SyntacticDiagnostic = {
82
- type: 'syntactic',
83
- fileName: rule.source!.input.file!,
86
+ const diagnostic: DiagnosticWithDetachedLocation = {
84
87
  ...calcDiagnosticsLocationForSelectorParserNode(rule, node),
85
88
  text: `A \`${node.value}(...)\` is not allowed inside of \`${wrappedBy}\`.`,
86
89
  category: 'error',
@@ -107,7 +110,7 @@ interface ClassSelector {
107
110
 
108
111
  interface ParseRuleResult {
109
112
  classSelectors: ClassSelector[];
110
- diagnostics: SyntacticDiagnostic[];
113
+ diagnostics: DiagnosticWithDetachedLocation[];
111
114
  }
112
115
 
113
116
  /**
package/src/path.ts CHANGED
@@ -33,8 +33,5 @@ export function parse(path: string): ParsedPath {
33
33
  return { root: slash(root), dir: slash(dir), base, name, ext };
34
34
  }
35
35
 
36
- // eslint-disable-next-line n/no-unsupported-features/node-builtins, @typescript-eslint/unbound-method
37
- export const matchesGlob = nodePath.matchesGlob;
38
-
39
36
  // eslint-disable-next-line @typescript-eslint/unbound-method
40
37
  export const isAbsolute = nodePath.isAbsolute;
package/src/resolver.ts CHANGED
@@ -2,21 +2,12 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
2
2
  import type { CompilerOptions } from 'typescript';
3
3
  import ts from 'typescript';
4
4
  import { isAbsolute, resolve } from './path.js';
5
+ import type { Resolver, ResolverOptions } from './type.js';
5
6
 
6
- export interface ResolverOptions {
7
- /** The file that imports the specifier. It is a absolute path. */
8
- request: string;
9
- }
10
-
11
- /**
12
- * A resolver function that resolves import specifiers.
13
- * @param specifier The import specifier.
14
- * @param options The options.
15
- * @returns The resolved import specifier. It is a absolute path. If the import specifier cannot be resolved, return `undefined`.
16
- */
17
- export type Resolver = (specifier: string, options: ResolverOptions) => string | undefined;
18
-
19
- export function createResolver(compilerOptions: CompilerOptions): Resolver {
7
+ export function createResolver(
8
+ compilerOptions: CompilerOptions,
9
+ moduleResolutionCache: ts.ModuleResolutionCache | undefined,
10
+ ): Resolver {
20
11
  return (_specifier: string, options: ResolverOptions) => {
21
12
  let specifier = _specifier;
22
13
 
@@ -29,7 +20,13 @@ export function createResolver(compilerOptions: CompilerOptions): Resolver {
29
20
  return ts.sys.fileExists(fileName);
30
21
  },
31
22
  };
32
- const { resolvedModule } = ts.resolveModuleName(specifier, options.request, compilerOptions, host);
23
+ const { resolvedModule } = ts.resolveModuleName(
24
+ specifier,
25
+ options.request,
26
+ compilerOptions,
27
+ host,
28
+ moduleResolutionCache,
29
+ );
33
30
  if (resolvedModule) {
34
31
  // TODO: Logging that the paths is used.
35
32
  specifier = resolvedModule.resolvedFileName.replace(/\.module\.d\.css\.ts$/u, '.module.css');