@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
package/dist/resolver.js CHANGED
@@ -7,7 +7,7 @@ exports.createResolver = createResolver;
7
7
  const node_url_1 = require("node:url");
8
8
  const typescript_1 = __importDefault(require("typescript"));
9
9
  const path_js_1 = require("./path.js");
10
- function createResolver(compilerOptions) {
10
+ function createResolver(compilerOptions, moduleResolutionCache) {
11
11
  return (_specifier, options) => {
12
12
  let specifier = _specifier;
13
13
  const host = {
@@ -19,7 +19,7 @@ function createResolver(compilerOptions) {
19
19
  return typescript_1.default.sys.fileExists(fileName);
20
20
  },
21
21
  };
22
- const { resolvedModule } = typescript_1.default.resolveModuleName(specifier, options.request, compilerOptions, host);
22
+ const { resolvedModule } = typescript_1.default.resolveModuleName(specifier, options.request, compilerOptions, host, moduleResolutionCache);
23
23
  if (resolvedModule) {
24
24
  // TODO: Logging that the paths is used.
25
25
  specifier = resolvedModule.resolvedFileName.replace(/\.module\.d\.css\.ts$/u, '.module.css');
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":";;;;;AAkBA,wCA+BC;AAjDD,uCAAwD;AAExD,4DAA4B;AAC5B,uCAAgD;AAehD,SAAgB,cAAc,CAAC,eAAgC;IAC7D,OAAO,CAAC,UAAkB,EAAE,OAAwB,EAAE,EAAE;QACtD,IAAI,SAAS,GAAG,UAAU,CAAC;QAE3B,MAAM,IAAI,GAA4B;YACpC,GAAG,oBAAE,CAAC,GAAG;YACT,UAAU,EAAE,CAAC,QAAQ,EAAE,EAAE;gBACvB,IAAI,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;oBAC1C,OAAO,oBAAE,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,wBAAwB,EAAE,aAAa,CAAC,CAAC,CAAC;gBACtF,CAAC;gBACD,OAAO,oBAAE,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACrC,CAAC;SACF,CAAC;QACF,MAAM,EAAE,cAAc,EAAE,GAAG,oBAAE,CAAC,iBAAiB,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,CAAC,CAAC;QACnG,IAAI,cAAc,EAAE,CAAC;YACnB,wCAAwC;YACxC,SAAS,GAAG,cAAc,CAAC,gBAAgB,CAAC,OAAO,CAAC,wBAAwB,EAAE,aAAa,CAAC,CAAC;QAC/F,CAAC;QACD,IAAI,IAAA,oBAAU,EAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAA,iBAAO,EAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;aAAM,IAAI,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1C,4CAA4C;YAC5C,kIAAkI;YAClI,qEAAqE;YACrE,OAAO,IAAA,iBAAO,EAAC,IAAA,wBAAa,EAAC,IAAI,GAAG,CAAC,SAAS,EAAE,IAAA,wBAAa,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACzF,CAAC;aAAM,CAAC;YACN,wCAAwC;YACxC,sDAAsD;YACtD,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,SAAiB;IAC5C,OAAO,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC"}
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":";;;;;AAMA,wCAwCC;AA9CD,uCAAwD;AAExD,4DAA4B;AAC5B,uCAAgD;AAGhD,SAAgB,cAAc,CAC5B,eAAgC,EAChC,qBAA2D;IAE3D,OAAO,CAAC,UAAkB,EAAE,OAAwB,EAAE,EAAE;QACtD,IAAI,SAAS,GAAG,UAAU,CAAC;QAE3B,MAAM,IAAI,GAA4B;YACpC,GAAG,oBAAE,CAAC,GAAG;YACT,UAAU,EAAE,CAAC,QAAQ,EAAE,EAAE;gBACvB,IAAI,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;oBAC1C,OAAO,oBAAE,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,wBAAwB,EAAE,aAAa,CAAC,CAAC,CAAC;gBACtF,CAAC;gBACD,OAAO,oBAAE,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACrC,CAAC;SACF,CAAC;QACF,MAAM,EAAE,cAAc,EAAE,GAAG,oBAAE,CAAC,iBAAiB,CAC7C,SAAS,EACT,OAAO,CAAC,OAAO,EACf,eAAe,EACf,IAAI,EACJ,qBAAqB,CACtB,CAAC;QACF,IAAI,cAAc,EAAE,CAAC;YACnB,wCAAwC;YACxC,SAAS,GAAG,cAAc,CAAC,gBAAgB,CAAC,OAAO,CAAC,wBAAwB,EAAE,aAAa,CAAC,CAAC;QAC/F,CAAC;QACD,IAAI,IAAA,oBAAU,EAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAA,iBAAO,EAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;aAAM,IAAI,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1C,4CAA4C;YAC5C,kIAAkI;YAClI,qEAAqE;YACrE,OAAO,IAAA,iBAAO,EAAC,IAAA,wBAAa,EAAC,IAAI,GAAG,CAAC,SAAS,EAAE,IAAA,wBAAa,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACzF,CAAC;aAAM,CAAC;YACN,wCAAwC;YACxC,sDAAsD;YACtD,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,SAAiB;IAC5C,OAAO,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC"}
package/dist/type.d.ts ADDED
@@ -0,0 +1,174 @@
1
+ /** The position of the node in the source file. */
2
+ export interface Position {
3
+ /**
4
+ * The line number in the source file. It is 1-based.
5
+ * This is compatible with postcss and tsserver.
6
+ */
7
+ line: number;
8
+ /**
9
+ * The column number in the source file. It is 1-based.
10
+ * This is compatible with postcss and tsserver.
11
+ */
12
+ column: number;
13
+ /** The offset in the source file. It is 0-based. */
14
+ offset: number;
15
+ }
16
+ /** The location of the node in the source file. */
17
+ export interface Location {
18
+ /**
19
+ * The starting position of the node. It is inclusive.
20
+ * This is compatible with postcss and tsserver.
21
+ */
22
+ start: Position;
23
+ /**
24
+ * The ending position of the node. It is exclusive.
25
+ * This is compatible with tsserver, but not postcss.
26
+ */
27
+ end: Position;
28
+ }
29
+ /** The item being exported from a CSS module file (a.k.a. token). */
30
+ export interface Token {
31
+ /** The token name. */
32
+ name: string;
33
+ /** The location of the token in the source file. */
34
+ loc: Location;
35
+ }
36
+ /**
37
+ * A token importer using `@import '...'`.
38
+ * `@import` imports all tokens from the file. Therefore, it does not have
39
+ * the name of the imported token unlike {@link AtValueTokenImporter}.
40
+ */
41
+ export interface AtImportTokenImporter {
42
+ type: 'import';
43
+ /**
44
+ * The specifier of the file from which the token is imported.
45
+ * This is a string before being resolved and unquoted.
46
+ * @example `@import './a.module.css'` would have `from` as `'./a.module.css'`.
47
+ */
48
+ from: string;
49
+ /** The location of the `from` in *.module.css file. */
50
+ fromLoc: Location;
51
+ }
52
+ /** A token importer using `@value ... from '...'`. */
53
+ export interface AtValueTokenImporter {
54
+ type: 'value';
55
+ /** The values imported from the file. */
56
+ values: AtValueTokenImporterValue[];
57
+ /**
58
+ * The specifier of the file from which the token is imported.
59
+ * This is a string before being resolved and unquoted.
60
+ * @example `@value a from './a.module.css'` would have `from` as `'./a.module.css'`.
61
+ */
62
+ from: string;
63
+ /** The location of the `from` in *.module.css file. */
64
+ fromLoc: Location;
65
+ }
66
+ /** A value imported from a CSS module file using `@value ... from '...'`. */
67
+ export interface AtValueTokenImporterValue {
68
+ /**
69
+ * The name of the token in the file from which it is imported.
70
+ * @example `@value a from './a.module.css'` would have `name` as `'a'`.
71
+ * @example `@value a as b from './a.module.css'` would have `name` as `'a'`.
72
+ */
73
+ name: string;
74
+ /** The location of the `name` in *.module.css file. */
75
+ loc: Location;
76
+ /**
77
+ * The name of the token in the current file.
78
+ * @example `@value a from './a.module.css'` would not have `localName`.
79
+ * @example `@value a as b from './a.module.css'` would have `localName` as `'b'`.
80
+ */
81
+ localName?: string;
82
+ /**
83
+ * The location of the `localName` in *.module.css file.
84
+ * This is `undefined` when `localName` is `undefined`.
85
+ */
86
+ localLoc?: Location;
87
+ }
88
+ export type TokenImporter = AtImportTokenImporter | AtValueTokenImporter;
89
+ export interface CSSModule {
90
+ /** Absolute path of the file */
91
+ fileName: string;
92
+ /** The content of the file */
93
+ text: string;
94
+ /**
95
+ * List of token names defined in the file.
96
+ * @example
97
+ * Consider the following file:
98
+ * ```css
99
+ * .foo { color: red }
100
+ * .bar, .baz { color: red }
101
+ * ```
102
+ * The `localTokens` for this file would be `['foo', 'bar', 'baz']`.
103
+ */
104
+ localTokens: Token[];
105
+ /**
106
+ * List of token importers in the file.
107
+ * Token importer is a statement that imports tokens from another file.
108
+ */
109
+ tokenImporters: TokenImporter[];
110
+ }
111
+ export interface ResolverOptions {
112
+ /** The file that imports the specifier. It is a absolute path. */
113
+ request: string;
114
+ }
115
+ /**
116
+ * A resolver function that resolves import specifiers.
117
+ * @param specifier The import specifier.
118
+ * @param options The options.
119
+ * @returns The resolved import specifier. It is a absolute path. If the import specifier cannot be resolved, return `undefined`.
120
+ */
121
+ export type Resolver = (specifier: string, options: ResolverOptions) => string | undefined;
122
+ /**
123
+ * A function that checks if a file name matches a pattern.
124
+ * @param fileName The file name. It is an absolute path.
125
+ * @returns `true` if the file name matches the pattern, otherwise `false`.
126
+ */
127
+ export type MatchesPattern = (fileName: string) => boolean;
128
+ /** The export token record of a CSS module. */
129
+ export interface ExportRecord {
130
+ /** The all exported tokens of the CSS module. */
131
+ allTokens: string[];
132
+ }
133
+ export interface ExportBuilder {
134
+ build(cssModule: CSSModule): ExportRecord;
135
+ clearCache(): void;
136
+ }
137
+ export type DiagnosticCategory = 'error' | 'warning';
138
+ export interface DiagnosticSourceFile {
139
+ fileName: string;
140
+ text: string;
141
+ }
142
+ export interface DiagnosticPosition {
143
+ /** The line number in the source file. It is 1-based. */
144
+ line: number;
145
+ /** The column number in the source file. It is 1-based. */
146
+ column: number;
147
+ }
148
+ interface DiagnosticWithoutLocation {
149
+ /** Text of diagnostic message. */
150
+ text: string;
151
+ /** The category of the diagnostic message. */
152
+ category: DiagnosticCategory;
153
+ }
154
+ export interface DiagnosticWithLocation extends DiagnosticWithoutLocation {
155
+ /** The file in which the diagnostic occurred */
156
+ file: DiagnosticSourceFile;
157
+ /** Starting file position at which text applies. It is inclusive. */
158
+ start: DiagnosticPosition;
159
+ /** Length of the diagnostic. */
160
+ length: number;
161
+ }
162
+ export type Diagnostic = DiagnosticWithLocation | DiagnosticWithoutLocation;
163
+ /**
164
+ * A diagnostic with location information detached from the source file.
165
+ * It is an intermediate representation used inside the CSS Module parser.
166
+ */
167
+ export interface DiagnosticWithDetachedLocation extends DiagnosticWithoutLocation {
168
+ /** Starting file position at which text applies. It is inclusive. */
169
+ start: DiagnosticPosition;
170
+ /** Length of the diagnostic. */
171
+ length: number;
172
+ }
173
+ export {};
174
+ //# sourceMappingURL=type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../src/type.ts"],"names":[],"mappings":"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"}
package/dist/type.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.js","sourceRoot":"","sources":["../src/type.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@css-modules-kit/core",
3
3
  "description": "The core of css-modules-kit",
4
- "version": "0.1.0",
4
+ "version": "0.3.0",
5
5
  "type": "commonjs",
6
6
  "sideEffects": false,
7
7
  "repository": {
@@ -24,7 +24,7 @@
24
24
  "build": "tsc -b tsconfig.build.json"
25
25
  },
26
26
  "engines": {
27
- "node": ">=22.0.0"
27
+ "node": ">=20.19.0"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public",
package/src/checker.ts CHANGED
@@ -1,13 +1,13 @@
1
- import type { SemanticDiagnostic } from './diagnostic.js';
2
- import type { ExportBuilder } from './export-builder.js';
3
- import type { MatchesPattern } from './file.js';
4
1
  import type {
5
2
  AtValueTokenImporter,
6
3
  AtValueTokenImporterValue,
7
4
  CSSModule,
5
+ Diagnostic,
6
+ ExportBuilder,
7
+ MatchesPattern,
8
+ Resolver,
8
9
  TokenImporter,
9
- } from './parser/css-module-parser.js';
10
- import type { Resolver } from './resolver.js';
10
+ } from './type.js';
11
11
 
12
12
  export function checkCSSModule(
13
13
  cssModule: CSSModule,
@@ -15,8 +15,8 @@ export function checkCSSModule(
15
15
  matchesPattern: MatchesPattern,
16
16
  resolver: Resolver,
17
17
  getCSSModule: (path: string) => CSSModule | undefined,
18
- ): SemanticDiagnostic[] {
19
- const diagnostics: SemanticDiagnostic[] = [];
18
+ ): Diagnostic[] {
19
+ const diagnostics: Diagnostic[] = [];
20
20
 
21
21
  for (const tokenImporter of cssModule.tokenImporters) {
22
22
  const from = resolver(tokenImporter.from, { request: cssModule.fileName });
@@ -39,14 +39,13 @@ export function checkCSSModule(
39
39
  return diagnostics;
40
40
  }
41
41
 
42
- function createCannotImportModuleDiagnostic(cssModule: CSSModule, tokenImporter: TokenImporter): SemanticDiagnostic {
42
+ function createCannotImportModuleDiagnostic(cssModule: CSSModule, tokenImporter: TokenImporter): Diagnostic {
43
43
  return {
44
- type: 'semantic',
45
44
  text: `Cannot import module '${tokenImporter.from}'`,
46
45
  category: 'error',
47
- fileName: cssModule.fileName,
46
+ file: { fileName: cssModule.fileName, text: cssModule.text },
48
47
  start: { line: tokenImporter.fromLoc.start.line, column: tokenImporter.fromLoc.start.column },
49
- end: { line: tokenImporter.fromLoc.end.line, column: tokenImporter.fromLoc.end.column },
48
+ length: tokenImporter.fromLoc.end.offset - tokenImporter.fromLoc.start.offset,
50
49
  };
51
50
  }
52
51
 
@@ -54,13 +53,12 @@ function createModuleHasNoExportedTokenDiagnostic(
54
53
  cssModule: CSSModule,
55
54
  tokenImporter: AtValueTokenImporter,
56
55
  value: AtValueTokenImporterValue,
57
- ): SemanticDiagnostic {
56
+ ): Diagnostic {
58
57
  return {
59
- type: 'semantic',
60
58
  text: `Module '${tokenImporter.from}' has no exported token '${value.name}'.`,
61
59
  category: 'error',
62
- fileName: cssModule.fileName,
60
+ file: { fileName: cssModule.fileName, text: cssModule.text },
63
61
  start: { line: value.loc.start.line, column: value.loc.start.column },
64
- end: { line: value.loc.end.line, column: value.loc.end.column },
62
+ length: value.loc.end.offset - value.loc.start.offset,
65
63
  };
66
64
  }
package/src/config.ts CHANGED
@@ -1,15 +1,11 @@
1
1
  import ts from 'typescript';
2
- import type { SemanticDiagnostic } from './diagnostic.js';
3
2
  import { TsConfigFileNotFoundError } from './error.js';
4
3
  import { basename, dirname, join, resolve } from './path.js';
4
+ import type { Diagnostic } from './type.js';
5
5
 
6
6
  // https://github.com/microsoft/TypeScript/blob/caf1aee269d1660b4d2a8b555c2d602c97cb28d7/src/compiler/commandLineParser.ts#L3006
7
7
  const DEFAULT_INCLUDE_SPEC = '**/*';
8
8
 
9
- type RemoveUndefined<T> = {
10
- [K in keyof T]: Exclude<T[K], undefined>;
11
- };
12
-
13
9
  /**
14
10
  * The config used by css-modules-kit.
15
11
  * This is normalized. Paths are resolved from relative to absolute, and default values are set for missing options.
@@ -19,6 +15,8 @@ export interface CMKConfig {
19
15
  excludes: string[];
20
16
  dtsOutDir: string;
21
17
  arbitraryExtensions: boolean;
18
+ namedExports: boolean;
19
+ prioritizeNamedImports: boolean;
22
20
  /**
23
21
  * A root directory to resolve relative path entries in the config file to.
24
22
  * This is an absolute path.
@@ -60,7 +58,7 @@ export interface CMKConfig {
60
58
  configFileName: string;
61
59
  compilerOptions: ts.CompilerOptions;
62
60
  /** The diagnostics that occurred while reading the config file. */
63
- diagnostics: SemanticDiagnostic[];
61
+ diagnostics: Diagnostic[];
64
62
  }
65
63
 
66
64
  /**
@@ -68,10 +66,12 @@ export interface CMKConfig {
68
66
  * This is unnormalized. Paths are relative, and some options may be omitted.
69
67
  */
70
68
  interface UnnormalizedRawConfig {
71
- includes: string[] | undefined;
72
- excludes: string[] | undefined;
73
- dtsOutDir: string | undefined;
74
- arbitraryExtensions: boolean | undefined;
69
+ includes?: string[];
70
+ excludes?: string[];
71
+ dtsOutDir?: string;
72
+ arbitraryExtensions?: boolean;
73
+ namedExports?: boolean;
74
+ prioritizeNamedImports?: boolean;
75
75
  }
76
76
 
77
77
  /**
@@ -79,7 +79,7 @@ interface UnnormalizedRawConfig {
79
79
  */
80
80
  interface ParsedRawData {
81
81
  config: UnnormalizedRawConfig;
82
- diagnostics: SemanticDiagnostic[];
82
+ diagnostics: Diagnostic[];
83
83
  }
84
84
 
85
85
  export function findTsConfigFile(project: string): string | undefined {
@@ -91,14 +91,9 @@ export function findTsConfigFile(project: string): string | undefined {
91
91
  return resolve(configFile);
92
92
  }
93
93
 
94
- function parseRawData(raw: unknown, configFileName: string): ParsedRawData {
94
+ function parseRawData(raw: unknown, tsConfigSourceFile: ts.TsConfigSourceFile): ParsedRawData {
95
95
  const result: ParsedRawData = {
96
- config: {
97
- includes: undefined,
98
- excludes: undefined,
99
- dtsOutDir: undefined,
100
- arbitraryExtensions: undefined,
101
- },
96
+ config: {},
102
97
  diagnostics: [],
103
98
  };
104
99
  if (typeof raw !== 'object' || raw === null) return result;
@@ -126,10 +121,9 @@ function parseRawData(raw: unknown, configFileName: string): ParsedRawData {
126
121
  result.config.dtsOutDir = raw.cmkOptions.dtsOutDir;
127
122
  } else {
128
123
  result.diagnostics.push({
129
- type: 'semantic',
130
124
  category: 'error',
131
- text: '`dtsOutDir` must be a string.',
132
- fileName: configFileName,
125
+ text: `\`dtsOutDir\` in ${tsConfigSourceFile.fileName} must be a string.`,
126
+ // MEMO: Location information can be obtained from `tsConfigSourceFile.statements`, but this is complicated and will be omitted.
133
127
  });
134
128
  }
135
129
  }
@@ -138,10 +132,31 @@ function parseRawData(raw: unknown, configFileName: string): ParsedRawData {
138
132
  result.config.arbitraryExtensions = raw.cmkOptions.arbitraryExtensions;
139
133
  } else {
140
134
  result.diagnostics.push({
141
- type: 'semantic',
142
135
  category: 'error',
143
- text: '`arbitraryExtensions` must be a boolean.',
144
- fileName: configFileName,
136
+ text: `\`arbitraryExtensions\` in ${tsConfigSourceFile.fileName} must be a boolean.`,
137
+ // MEMO: Location information can be obtained from `tsConfigSourceFile.statements`, but this is complicated and will be omitted.
138
+ });
139
+ }
140
+ }
141
+ if ('namedExports' in raw.cmkOptions) {
142
+ if (typeof raw.cmkOptions.namedExports === 'boolean') {
143
+ result.config.namedExports = raw.cmkOptions.namedExports;
144
+ } else {
145
+ result.diagnostics.push({
146
+ category: 'error',
147
+ text: `\`namedExports\` in ${tsConfigSourceFile.fileName} must be a boolean.`,
148
+ // MEMO: Location information can be obtained from `tsConfigSourceFile.statements`, but this is complicated and will be omitted.
149
+ });
150
+ }
151
+ }
152
+ if ('prioritizeNamedImports' in raw.cmkOptions) {
153
+ if (typeof raw.cmkOptions.prioritizeNamedImports === 'boolean') {
154
+ result.config.prioritizeNamedImports = raw.cmkOptions.prioritizeNamedImports;
155
+ } else {
156
+ result.diagnostics.push({
157
+ category: 'error',
158
+ text: `\`prioritizeNamedImports\` in ${tsConfigSourceFile.fileName} must be a boolean.`,
159
+ // MEMO: Location information can be obtained from `tsConfigSourceFile.statements`, but this is complicated and will be omitted.
145
160
  });
146
161
  }
147
162
  }
@@ -157,6 +172,7 @@ function mergeParsedRawData(base: ParsedRawData, overrides: ParsedRawData): Pars
157
172
  if (overrides.config.dtsOutDir !== undefined) result.config.dtsOutDir = overrides.config.dtsOutDir;
158
173
  if (overrides.config.arbitraryExtensions !== undefined)
159
174
  result.config.arbitraryExtensions = overrides.config.arbitraryExtensions;
175
+ if (overrides.config.namedExports !== undefined) result.config.namedExports = overrides.config.namedExports;
160
176
  result.diagnostics.push(...overrides.diagnostics);
161
177
  return result;
162
178
  }
@@ -168,7 +184,7 @@ export function readTsConfigFile(project: string): {
168
184
  configFileName: string;
169
185
  config: UnnormalizedRawConfig;
170
186
  compilerOptions: ts.CompilerOptions;
171
- diagnostics: SemanticDiagnostic[];
187
+ diagnostics: Diagnostic[];
172
188
  } {
173
189
  const configFileName = findTsConfigFile(project);
174
190
  if (!configFileName) throw new TsConfigFileNotFoundError();
@@ -195,7 +211,7 @@ export function readTsConfigFile(project: string): {
195
211
  ],
196
212
  );
197
213
  // Read options from `parsedCommandLine.raw`
198
- let parsedRawData = parseRawData(parsedCommandLine.raw, configFileName);
214
+ let parsedRawData = parseRawData(parsedCommandLine.raw, tsConfigSourceFile);
199
215
 
200
216
  // The options read from `parsedCommandLine.raw` do not inherit values from the file specified in `extends`.
201
217
  // So here we read the options from those files and merge them into `parsedRawData`.
@@ -219,24 +235,6 @@ export function readTsConfigFile(project: string): {
219
235
  };
220
236
  }
221
237
 
222
- /**
223
- * Normalize `UnnormalizedRawConfig`. Resolve relative paths to absolute paths, and set default values for missing options.
224
- * @param basePath A root directory to resolve relative path entries in the config file to.
225
- */
226
- export function normalizeConfig(
227
- config: UnnormalizedRawConfig,
228
- basePath: string,
229
- ): RemoveUndefined<UnnormalizedRawConfig> {
230
- return {
231
- // If `include` is not specified, fallback to the default include spec.
232
- // ref: https://github.com/microsoft/TypeScript/blob/caf1aee269d1660b4d2a8b555c2d602c97cb28d7/src/compiler/commandLineParser.ts#L3102
233
- includes: (config.includes ?? [DEFAULT_INCLUDE_SPEC]).map((i) => join(basePath, i)),
234
- excludes: (config.excludes ?? []).map((e) => join(basePath, e)),
235
- dtsOutDir: join(basePath, config.dtsOutDir ?? 'generated'),
236
- arbitraryExtensions: config.arbitraryExtensions ?? false,
237
- };
238
- }
239
-
240
238
  /**
241
239
  * Reads the `tsconfig.json` file and returns the normalized config.
242
240
  * Even if the `tsconfig.json` file contains syntax or semantic errors,
@@ -249,7 +247,14 @@ export function readConfigFile(project: string): CMKConfig {
249
247
  const { configFileName, config, compilerOptions, diagnostics } = readTsConfigFile(project);
250
248
  const basePath = dirname(configFileName);
251
249
  return {
252
- ...normalizeConfig(config, basePath),
250
+ // If `include` is not specified, fallback to the default include spec.
251
+ // ref: https://github.com/microsoft/TypeScript/blob/caf1aee269d1660b4d2a8b555c2d602c97cb28d7/src/compiler/commandLineParser.ts#L3102
252
+ includes: (config.includes ?? [DEFAULT_INCLUDE_SPEC]).map((i) => join(basePath, i)),
253
+ excludes: (config.excludes ?? []).map((e) => join(basePath, e)),
254
+ dtsOutDir: join(basePath, config.dtsOutDir ?? 'generated'),
255
+ arbitraryExtensions: config.arbitraryExtensions ?? false,
256
+ namedExports: config.namedExports ?? false,
257
+ prioritizeNamedImports: config.prioritizeNamedImports ?? false,
253
258
  basePath,
254
259
  configFileName,
255
260
  compilerOptions,
package/src/diagnostic.ts CHANGED
@@ -1,37 +1,77 @@
1
- export type DiagnosticCategory = 'error' | 'warning';
1
+ import ts from 'typescript';
2
+ import type { SystemError } from './error.js';
3
+ import type { Diagnostic, DiagnosticSourceFile, DiagnosticWithLocation } from './type.js';
2
4
 
3
- export interface DiagnosticPosition {
4
- /** The line number in the source file. It is 1-based. */
5
- line: number;
6
- /** The column number in the source file. It is 1-based. */
7
- column: number;
8
- }
5
+ /** The error code used by tsserver to display the css-modules-kit error in the editor. */
6
+ const TS_ERROR_CODE = 0;
7
+
8
+ const TS_ERROR_SOURCE = 'css-modules-kit';
9
9
 
10
- export type Diagnostic = SemanticDiagnostic | SyntacticDiagnostic;
10
+ function convertErrorCategory(category: 'error' | 'warning' | 'suggestion'): ts.DiagnosticCategory {
11
+ switch (category) {
12
+ case 'error':
13
+ return ts.DiagnosticCategory.Error;
14
+ case 'warning':
15
+ return ts.DiagnosticCategory.Warning;
16
+ case 'suggestion':
17
+ return ts.DiagnosticCategory.Suggestion;
18
+ default:
19
+ throw new Error(`Unknown category: ${String(category)}`);
20
+ }
21
+ }
11
22
 
12
- interface DiagnosticBase {
13
- /** Text of diagnostic message. */
14
- text: string;
15
- /** The category of the diagnostic message. */
16
- category: DiagnosticCategory;
23
+ export function convertDiagnostic(
24
+ diagnostic: Diagnostic,
25
+ getSourceFile: (file: DiagnosticSourceFile) => ts.SourceFile,
26
+ ): ts.Diagnostic {
27
+ if ('file' in diagnostic) {
28
+ return convertDiagnosticWithLocation(diagnostic, getSourceFile);
29
+ } else {
30
+ return {
31
+ file: undefined,
32
+ start: undefined,
33
+ length: undefined,
34
+ category: convertErrorCategory(diagnostic.category),
35
+ messageText: diagnostic.text,
36
+ code: TS_ERROR_CODE,
37
+ source: TS_ERROR_SOURCE,
38
+ };
39
+ }
17
40
  }
18
41
 
19
- export interface SemanticDiagnostic extends DiagnosticBase {
20
- type: 'semantic';
21
- /** The filename of the file in which the diagnostic occurred */
22
- fileName?: string;
23
- /** Starting file position at which text applies. It is inclusive. */
24
- start?: DiagnosticPosition;
25
- /** The last file position at which the text applies. It is exclusive. */
26
- end?: DiagnosticPosition;
42
+ export function convertDiagnosticWithLocation(
43
+ diagnostic: DiagnosticWithLocation,
44
+ getSourceFile: (file: DiagnosticSourceFile) => ts.SourceFile,
45
+ ): ts.DiagnosticWithLocation {
46
+ const sourceFile = getSourceFile(diagnostic.file);
47
+ const start = ts.getPositionOfLineAndCharacter(sourceFile, diagnostic.start.line - 1, diagnostic.start.column - 1);
48
+ return {
49
+ file: sourceFile,
50
+ start,
51
+ length: diagnostic.length,
52
+ category: convertErrorCategory(diagnostic.category),
53
+ messageText: diagnostic.text,
54
+ code: TS_ERROR_CODE,
55
+ source: TS_ERROR_SOURCE,
56
+ };
27
57
  }
28
58
 
29
- export interface SyntacticDiagnostic extends DiagnosticBase {
30
- type: 'syntactic';
31
- /** The filename of the file in which the diagnostic occurred */
32
- fileName: string;
33
- /** Starting file position at which text applies. It is inclusive. */
34
- start: DiagnosticPosition;
35
- /** The last file position at which the text applies. It is exclusive. */
36
- end?: DiagnosticPosition;
59
+ export function convertSystemError(systemError: SystemError): ts.Diagnostic {
60
+ let messageText = systemError.message;
61
+ if (systemError.cause) {
62
+ if (systemError.cause instanceof Error) {
63
+ messageText += `: ${systemError.cause.message}`;
64
+ } else {
65
+ messageText += `: ${JSON.stringify(systemError.cause)}`;
66
+ }
67
+ }
68
+ return {
69
+ file: undefined,
70
+ start: undefined,
71
+ length: undefined,
72
+ category: ts.DiagnosticCategory.Error,
73
+ messageText,
74
+ code: TS_ERROR_CODE,
75
+ source: TS_ERROR_SOURCE,
76
+ };
37
77
  }