@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.
- 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 +13 -17
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +41 -30
- 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 +8 -28
- package/dist/dts-creator.d.ts.map +1 -1
- package/dist/dts-creator.js +106 -7
- 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 +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- 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/path.d.ts +0 -1
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +1 -3
- package/dist/path.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 +2 -2
- package/src/checker.ts +13 -15
- package/src/config.ts +50 -45
- package/src/diagnostic.ts +69 -29
- package/src/dts-creator.ts +120 -13
- package/src/export-builder.ts +1 -16
- package/src/file.ts +1 -2
- package/src/index.ts +14 -14
- 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/path.ts +0 -3
- 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/dts-creator.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
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, 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
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
//
|
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 {
|
|
39
|
-
export { join, resolve, relative, dirname, basename, parse,
|
|
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,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/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
|
|
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');
|