@css-modules-kit/core 0.1.0 → 0.2.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/dist/checker.d.ts +2 -6
- package/dist/checker.d.ts.map +1 -1
- package/dist/checker.js +4 -6
- package/dist/checker.js.map +1 -1
- package/dist/config.d.ts +5 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -8
- package/dist/config.js.map +1 -1
- package/dist/diagnostic.d.ts +6 -33
- package/dist/diagnostic.d.ts.map +1 -1
- package/dist/diagnostic.js +71 -0
- package/dist/diagnostic.js.map +1 -1
- package/dist/dts-creator.d.ts +1 -3
- package/dist/dts-creator.d.ts.map +1 -1
- package/dist/dts-creator.js.map +1 -1
- package/dist/export-builder.d.ts +1 -14
- package/dist/export-builder.d.ts.map +1 -1
- package/dist/export-builder.js.map +1 -1
- package/dist/file.d.ts +1 -1
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/parser/at-import-parser.d.ts +1 -1
- package/dist/parser/at-import-parser.d.ts.map +1 -1
- package/dist/parser/at-value-parser.d.ts +2 -3
- package/dist/parser/at-value-parser.d.ts.map +1 -1
- package/dist/parser/at-value-parser.js +2 -13
- package/dist/parser/at-value-parser.js.map +1 -1
- package/dist/parser/css-module-parser.d.ts +2 -83
- package/dist/parser/css-module-parser.d.ts.map +1 -1
- package/dist/parser/css-module-parser.js +7 -8
- package/dist/parser/css-module-parser.js.map +1 -1
- package/dist/parser/rule-parser.d.ts +8 -4
- package/dist/parser/rule-parser.d.ts.map +1 -1
- package/dist/parser/rule-parser.js +9 -10
- package/dist/parser/rule-parser.js.map +1 -1
- package/dist/resolver.d.ts +3 -12
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +2 -2
- package/dist/resolver.js.map +1 -1
- package/dist/type.d.ts +174 -0
- package/dist/type.d.ts.map +1 -0
- package/dist/type.js +3 -0
- package/dist/type.js.map +1 -0
- package/package.json +1 -1
- package/src/checker.ts +13 -15
- package/src/config.ts +10 -12
- package/src/diagnostic.ts +69 -29
- package/src/dts-creator.ts +1 -3
- package/src/export-builder.ts +1 -16
- package/src/file.ts +1 -2
- package/src/index.ts +13 -13
- package/src/parser/at-import-parser.ts +1 -1
- package/src/parser/at-value-parser.ts +5 -17
- package/src/parser/css-module-parser.ts +16 -98
- package/src/parser/rule-parser.ts +17 -14
- package/src/resolver.ts +12 -15
- package/src/type.ts +191 -0
- package/dist/parser/location.d.ts +0 -34
- package/dist/parser/location.d.ts.map +0 -1
- package/dist/parser/location.js +0 -9
- package/dist/parser/location.js.map +0 -1
- package/src/parser/location.ts +0 -40
package/src/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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 = '**/*';
|
|
@@ -60,7 +60,7 @@ export interface CMKConfig {
|
|
|
60
60
|
configFileName: string;
|
|
61
61
|
compilerOptions: ts.CompilerOptions;
|
|
62
62
|
/** The diagnostics that occurred while reading the config file. */
|
|
63
|
-
diagnostics:
|
|
63
|
+
diagnostics: Diagnostic[];
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
@@ -79,7 +79,7 @@ interface UnnormalizedRawConfig {
|
|
|
79
79
|
*/
|
|
80
80
|
interface ParsedRawData {
|
|
81
81
|
config: UnnormalizedRawConfig;
|
|
82
|
-
diagnostics:
|
|
82
|
+
diagnostics: Diagnostic[];
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export function findTsConfigFile(project: string): string | undefined {
|
|
@@ -91,7 +91,7 @@ export function findTsConfigFile(project: string): string | undefined {
|
|
|
91
91
|
return resolve(configFile);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
function parseRawData(raw: unknown,
|
|
94
|
+
function parseRawData(raw: unknown, tsConfigSourceFile: ts.TsConfigSourceFile): ParsedRawData {
|
|
95
95
|
const result: ParsedRawData = {
|
|
96
96
|
config: {
|
|
97
97
|
includes: undefined,
|
|
@@ -126,10 +126,9 @@ function parseRawData(raw: unknown, configFileName: string): ParsedRawData {
|
|
|
126
126
|
result.config.dtsOutDir = raw.cmkOptions.dtsOutDir;
|
|
127
127
|
} else {
|
|
128
128
|
result.diagnostics.push({
|
|
129
|
-
type: 'semantic',
|
|
130
129
|
category: 'error',
|
|
131
|
-
text:
|
|
132
|
-
|
|
130
|
+
text: `\`dtsOutDir\` in ${tsConfigSourceFile.fileName} must be a string.`,
|
|
131
|
+
// MEMO: Location information can be obtained from `tsConfigSourceFile.statements`, but this is complicated and will be omitted.
|
|
133
132
|
});
|
|
134
133
|
}
|
|
135
134
|
}
|
|
@@ -138,10 +137,9 @@ function parseRawData(raw: unknown, configFileName: string): ParsedRawData {
|
|
|
138
137
|
result.config.arbitraryExtensions = raw.cmkOptions.arbitraryExtensions;
|
|
139
138
|
} else {
|
|
140
139
|
result.diagnostics.push({
|
|
141
|
-
type: 'semantic',
|
|
142
140
|
category: 'error',
|
|
143
|
-
text:
|
|
144
|
-
|
|
141
|
+
text: `\`arbitraryExtensions\` in ${tsConfigSourceFile.fileName} must be a boolean.`,
|
|
142
|
+
// MEMO: Location information can be obtained from `tsConfigSourceFile.statements`, but this is complicated and will be omitted.
|
|
145
143
|
});
|
|
146
144
|
}
|
|
147
145
|
}
|
|
@@ -168,7 +166,7 @@ export function readTsConfigFile(project: string): {
|
|
|
168
166
|
configFileName: string;
|
|
169
167
|
config: UnnormalizedRawConfig;
|
|
170
168
|
compilerOptions: ts.CompilerOptions;
|
|
171
|
-
diagnostics:
|
|
169
|
+
diagnostics: Diagnostic[];
|
|
172
170
|
} {
|
|
173
171
|
const configFileName = findTsConfigFile(project);
|
|
174
172
|
if (!configFileName) throw new TsConfigFileNotFoundError();
|
|
@@ -195,7 +193,7 @@ export function readTsConfigFile(project: string): {
|
|
|
195
193
|
],
|
|
196
194
|
);
|
|
197
195
|
// Read options from `parsedCommandLine.raw`
|
|
198
|
-
let parsedRawData = parseRawData(parsedCommandLine.raw,
|
|
196
|
+
let parsedRawData = parseRawData(parsedCommandLine.raw, tsConfigSourceFile);
|
|
199
197
|
|
|
200
198
|
// The options read from `parsedCommandLine.raw` do not inherit values from the file specified in `extends`.
|
|
201
199
|
// So here we read the options from those files and merge them into `parsedRawData`.
|
package/src/diagnostic.ts
CHANGED
|
@@ -1,37 +1,77 @@
|
|
|
1
|
-
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import type { SystemError } from './error.js';
|
|
3
|
+
import type { Diagnostic, DiagnosticSourceFile, DiagnosticWithLocation } from './type.js';
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
start
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
}
|
package/src/dts-creator.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { MatchesPattern } from './
|
|
2
|
-
import type { CSSModule } from './parser/css-module-parser.js';
|
|
3
|
-
import type { Resolver } from './resolver.js';
|
|
1
|
+
import type { CSSModule, MatchesPattern, Resolver } from './type.js';
|
|
4
2
|
|
|
5
3
|
export const STYLES_EXPORT_NAME = 'styles';
|
|
6
4
|
|
package/src/export-builder.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { MatchesPattern } from './
|
|
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
|
-
|
|
6
|
-
type
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export {
|
|
15
|
+
type Resolver,
|
|
16
|
+
type MatchesPattern,
|
|
17
|
+
type ExportBuilder,
|
|
18
|
+
type DiagnosticSourceFile,
|
|
18
19
|
type Diagnostic,
|
|
19
|
-
type
|
|
20
|
-
type SyntacticDiagnostic,
|
|
20
|
+
type DiagnosticWithLocation,
|
|
21
21
|
type DiagnosticCategory,
|
|
22
22
|
type DiagnosticPosition,
|
|
23
|
-
} from './
|
|
23
|
+
} from './type.js';
|
|
24
24
|
export { type CreateDtsOptions, createDts, STYLES_EXPORT_NAME } from './dts-creator.js';
|
|
25
|
-
export { createResolver
|
|
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 {
|
|
37
|
+
export { createExportBuilder } from './export-builder.js';
|
|
39
38
|
export { join, resolve, relative, dirname, basename, parse, matchesGlob, isAbsolute } from './path.js';
|
|
40
39
|
export { findUsedTokenNames } from './util.js';
|
|
40
|
+
export { convertDiagnostic, convertDiagnosticWithLocation, convertSystemError } from './diagnostic.js';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { AtRule } from 'postcss';
|
|
2
|
-
import type { DiagnosticPosition,
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
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:
|
|
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
|
-
|
|
171
|
-
fileName,
|
|
90
|
+
file: diagnosticSourceFile,
|
|
172
91
|
start,
|
|
173
|
-
|
|
174
|
-
|
|
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 {
|
|
4
|
-
|
|
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:
|
|
17
|
+
diagnostics: DiagnosticWithDetachedLocation[];
|
|
9
18
|
}
|
|
10
19
|
|
|
11
20
|
function flatCollectResults(results: CollectResult[]): CollectResult {
|
|
12
21
|
const classNames: selectorParser.ClassName[] = [];
|
|
13
|
-
const diagnostics:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
113
|
+
diagnostics: DiagnosticWithDetachedLocation[];
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
/**
|
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
|
|
7
|
-
|
|
8
|
-
|
|
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(
|
|
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');
|