happy-css-modules 0.3.0 → 0.4.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/README.md +15 -18
- package/dist/cli.js +15 -4
- package/dist/cli.js.map +1 -1
- package/dist/cli.test.js +10 -0
- package/dist/cli.test.js.map +1 -1
- package/dist/integration-test/go-to-definition.test.js +9 -8
- package/dist/integration-test/go-to-definition.test.js.map +1 -1
- package/dist/resolver/index.d.ts +3 -1
- package/dist/resolver/index.js +17 -14
- package/dist/resolver/index.js.map +1 -1
- package/dist/resolver/webpack-resolver.d.ts +13 -1
- package/dist/resolver/webpack-resolver.js +73 -59
- package/dist/resolver/webpack-resolver.js.map +1 -1
- package/dist/resolver/webpack-resolver.test.js +34 -7
- package/dist/resolver/webpack-resolver.test.js.map +1 -1
- package/dist/runner.d.ts +12 -0
- package/dist/runner.js +16 -10
- package/dist/runner.js.map +1 -1
- package/dist/runner.test.js +33 -9
- package/dist/runner.test.js.map +1 -1
- package/dist/test/tsserver.d.ts +10 -6
- package/dist/test/tsserver.js +94 -85
- package/dist/test/tsserver.js.map +1 -1
- package/dist/transformer/index.js +11 -9
- package/dist/transformer/index.js.map +1 -1
- package/dist/transformer/less-transformer.js +1 -2
- package/dist/transformer/less-transformer.js.map +1 -1
- package/dist/transformer/scss-transformer.js +0 -47
- package/dist/transformer/scss-transformer.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.test.ts +14 -0
- package/src/cli.ts +15 -4
- package/src/integration-test/go-to-definition.test.ts +10 -8
- package/src/resolver/index.ts +21 -12
- package/src/resolver/webpack-resolver.test.ts +44 -8
- package/src/resolver/webpack-resolver.ts +90 -57
- package/src/runner.test.ts +35 -9
- package/src/runner.ts +29 -10
- package/src/test/tsserver.ts +106 -129
- package/src/transformer/index.ts +10 -8
- package/src/transformer/less-transformer.ts +1 -2
- package/src/transformer/scss-transformer.ts +0 -51
package/src/test/tsserver.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { readFileSync } from 'fs';
|
|
2
|
+
import { mkdir, writeFile as nativeWriteFile } from 'fs/promises';
|
|
3
|
+
import { dirname } from 'path';
|
|
2
4
|
import { fileURLToPath } from 'url';
|
|
5
|
+
import { promisify } from 'util';
|
|
3
6
|
import serverHarness from '@typescript/server-harness';
|
|
7
|
+
import _glob from 'glob';
|
|
4
8
|
import { resolve } from 'import-meta-resolve';
|
|
5
9
|
import lineColumn from 'line-column';
|
|
10
|
+
import type { UpdateOpenRequest, DefinitionResponse, DefinitionRequest } from 'typescript/lib/protocol.js';
|
|
6
11
|
import { getFixturePath } from './util.js';
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
const glob = promisify(_glob);
|
|
14
|
+
|
|
15
|
+
async function writeFile(path: string, content: string): Promise<void> {
|
|
16
|
+
await mkdir(dirname(path), { recursive: true });
|
|
17
|
+
return nativeWriteFile(path, content, 'utf8');
|
|
18
|
+
}
|
|
9
19
|
|
|
10
20
|
type Definition = {
|
|
11
21
|
/** The path of the destination file */
|
|
@@ -28,42 +38,7 @@ type Definition = {
|
|
|
28
38
|
};
|
|
29
39
|
};
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
seq: number;
|
|
33
|
-
type: 'response';
|
|
34
|
-
command: 'definition';
|
|
35
|
-
success: boolean;
|
|
36
|
-
body: [
|
|
37
|
-
{
|
|
38
|
-
/** The path of the destination file */
|
|
39
|
-
file: string;
|
|
40
|
-
/** inclusive */
|
|
41
|
-
start: {
|
|
42
|
-
/** line, 1-based */
|
|
43
|
-
line: number;
|
|
44
|
-
/** column, 1-based */
|
|
45
|
-
offset: number;
|
|
46
|
-
};
|
|
47
|
-
/** exclusive */
|
|
48
|
-
end: {
|
|
49
|
-
/** line, 1-based */
|
|
50
|
-
line: number;
|
|
51
|
-
/** column, 1-based */
|
|
52
|
-
offset: number;
|
|
53
|
-
};
|
|
54
|
-
},
|
|
55
|
-
];
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export async function getIdentifierDefinitions(filePath: string, identifier: string): Promise<Definition[]> {
|
|
59
|
-
const results = await getMultipleIdentifierDefinitions(filePath, [identifier]);
|
|
60
|
-
return results[0]!.definitions;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export async function getMultipleIdentifierDefinitions(
|
|
64
|
-
filePath: string,
|
|
65
|
-
identifiers: string[],
|
|
66
|
-
): Promise<{ identifier: string; definitions: Definition[] }[]> {
|
|
41
|
+
export async function createTSServer() {
|
|
67
42
|
const server = serverHarness.launchServer(
|
|
68
43
|
fileURLToPath(await resolve('typescript/lib/tsserver.js', import.meta.url)),
|
|
69
44
|
[
|
|
@@ -72,105 +47,107 @@ export async function getMultipleIdentifierDefinitions(
|
|
|
72
47
|
],
|
|
73
48
|
);
|
|
74
49
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
].join('\n');
|
|
80
|
-
|
|
81
|
-
await server.message({
|
|
82
|
-
type: 'request',
|
|
83
|
-
command: 'updateOpen',
|
|
84
|
-
arguments: {
|
|
85
|
-
changedFiles: [],
|
|
86
|
-
closedFiles: [],
|
|
87
|
-
openFiles: [
|
|
88
|
-
{
|
|
89
|
-
file: tmpFilePath,
|
|
90
|
-
fileContent: tmpFileContent,
|
|
91
|
-
projectRootPath: getFixturePath('/server-harness'),
|
|
92
|
-
scriptKindName: 'TS', // It's easy to get this wrong when copy-pasting
|
|
93
|
-
},
|
|
94
|
-
],
|
|
50
|
+
return {
|
|
51
|
+
async getIdentifierDefinitions(filePath: string, identifier: string): Promise<Definition[]> {
|
|
52
|
+
const results = await this.getMultipleIdentifierDefinitions(filePath, [identifier]);
|
|
53
|
+
return results[0]!.definitions;
|
|
95
54
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
55
|
+
async getMultipleIdentifierDefinitions(
|
|
56
|
+
filePath: string,
|
|
57
|
+
identifiers: string[],
|
|
58
|
+
): Promise<{ identifier: string; definitions: Definition[] }[]> {
|
|
59
|
+
const tmpFilePath = getFixturePath('/server-harness/tmp.ts');
|
|
60
|
+
const tmpFileContent = [
|
|
61
|
+
`import styles from '${filePath}';`,
|
|
62
|
+
...identifiers.map((identifier) => `styles.${identifier};`),
|
|
63
|
+
].join('\n');
|
|
64
|
+
await writeFile(tmpFilePath, tmpFileContent);
|
|
65
|
+
|
|
66
|
+
await this.refreshCache();
|
|
67
|
+
|
|
68
|
+
const results: { identifier: string; definitions: Definition[] }[] = [];
|
|
69
|
+
|
|
70
|
+
for (let i = 0; i < identifiers.length; i++) {
|
|
71
|
+
const response: DefinitionResponse = await server.message({
|
|
72
|
+
seq: 0,
|
|
73
|
+
type: 'request',
|
|
74
|
+
command: 'definition',
|
|
75
|
+
arguments: {
|
|
76
|
+
file: tmpFilePath,
|
|
77
|
+
line: i + 2, // line, 1-based
|
|
78
|
+
offset: 8, // column, 1-based
|
|
79
|
+
},
|
|
80
|
+
} as DefinitionRequest);
|
|
81
|
+
const definitions: Definition[] = response.body!.map((definition) => {
|
|
82
|
+
const { file, start, end } = definition;
|
|
83
|
+
const fileContent = readFileSync(file, 'utf-8');
|
|
84
|
+
const startIndex = lineColumn(fileContent).toIndex(start.line, start.offset);
|
|
85
|
+
const endIndex = lineColumn(fileContent).toIndex(end.line, end.offset);
|
|
86
|
+
const text = fileContent.slice(startIndex, endIndex);
|
|
87
|
+
return { file, text, start, end };
|
|
88
|
+
});
|
|
89
|
+
results.push({ identifier: identifiers[i]!, definitions });
|
|
90
|
+
}
|
|
91
|
+
return results;
|
|
92
|
+
},
|
|
93
|
+
async getModuleDefinitions(filePath: string): Promise<Definition[]> {
|
|
94
|
+
await this.refreshCache();
|
|
122
95
|
|
|
123
|
-
|
|
124
|
-
}
|
|
96
|
+
const tmpFilePath = getFixturePath('/server-harness/tmp.ts');
|
|
97
|
+
const tmpFileContent = `import styles from '${filePath}';`;
|
|
125
98
|
|
|
126
|
-
|
|
127
|
-
const server = serverHarness.launchServer(
|
|
128
|
-
fileURLToPath(await resolve('typescript/lib/tsserver.js', import.meta.url)),
|
|
129
|
-
[
|
|
130
|
-
// ATA generates some extra network traffic and isn't usually relevant when profiling
|
|
131
|
-
'--disableAutomaticTypingAcquisition',
|
|
132
|
-
],
|
|
133
|
-
);
|
|
99
|
+
await writeFile(tmpFilePath, tmpFileContent);
|
|
134
100
|
|
|
135
|
-
|
|
136
|
-
const tmpFileContent = `import styles from '${filePath}';`;
|
|
101
|
+
await this.refreshCache();
|
|
137
102
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
closedFiles: [],
|
|
144
|
-
openFiles: [
|
|
145
|
-
{
|
|
103
|
+
const response: DefinitionResponse = await server.message({
|
|
104
|
+
seq: 0,
|
|
105
|
+
type: 'request',
|
|
106
|
+
command: 'definition',
|
|
107
|
+
arguments: {
|
|
146
108
|
file: tmpFilePath,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
scriptKindName: 'TS', // It's easy to get this wrong when copy-pasting
|
|
109
|
+
line: 1, // line, 1-based
|
|
110
|
+
offset: 20, // column, 1-based
|
|
150
111
|
},
|
|
151
|
-
|
|
112
|
+
} as DefinitionRequest);
|
|
113
|
+
const definitions: Definition[] = response.body!.map((definition) => {
|
|
114
|
+
const { file, start, end } = definition;
|
|
115
|
+
const fileContent = readFileSync(file, 'utf-8');
|
|
116
|
+
const startIndex = lineColumn(fileContent).toIndex(start.line, start.offset);
|
|
117
|
+
const endIndex = lineColumn(fileContent).toIndex(end.line, end.offset);
|
|
118
|
+
const text = fileContent.slice(startIndex, endIndex);
|
|
119
|
+
return { file, text, start, end };
|
|
120
|
+
});
|
|
121
|
+
return definitions;
|
|
152
122
|
},
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
123
|
+
async refreshCache() {
|
|
124
|
+
// tsserver caches the contents of opened files.
|
|
125
|
+
// When a file is updated, its cache remains with the old content.
|
|
126
|
+
// Therefore we need to overwrite the cache with the latest content.
|
|
127
|
+
|
|
128
|
+
const fixtureFilePaths = await glob(getFixturePath('/**/*.ts'), { dot: true });
|
|
129
|
+
// latest contents
|
|
130
|
+
const openFiles: UpdateOpenRequest['arguments']['openFiles'] = fixtureFilePaths.map((filePath) => ({
|
|
131
|
+
file: filePath,
|
|
132
|
+
fileContent: readFileSync(filePath, 'utf-8'),
|
|
133
|
+
projectRootPath: getFixturePath('/server-harness'),
|
|
134
|
+
scriptKindName: 'TS', // It's easy to get this wrong when copy-pasting
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
// override the cache
|
|
138
|
+
await server.message({
|
|
139
|
+
seq: 0,
|
|
140
|
+
type: 'request',
|
|
141
|
+
command: 'updateOpen',
|
|
142
|
+
arguments: {
|
|
143
|
+
changedFiles: [],
|
|
144
|
+
closedFiles: [],
|
|
145
|
+
openFiles,
|
|
146
|
+
},
|
|
147
|
+
} as UpdateOpenRequest);
|
|
162
148
|
},
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const startIndex = lineColumn(fileContent).toIndex(start.line, start.offset);
|
|
168
|
-
const endIndex = lineColumn(fileContent).toIndex(end.line, end.offset);
|
|
169
|
-
const text = fileContent.slice(startIndex, endIndex);
|
|
170
|
-
return { file, text, start, end };
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
await server.message({ command: 'exit' });
|
|
174
|
-
|
|
175
|
-
return definitions;
|
|
149
|
+
exit: async () => {
|
|
150
|
+
await server.message({ command: 'exit' });
|
|
151
|
+
},
|
|
152
|
+
};
|
|
176
153
|
}
|
package/src/transformer/index.ts
CHANGED
|
@@ -36,14 +36,16 @@ export const handleImportError = (packageName: string) => (e: unknown) => {
|
|
|
36
36
|
throw e;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
export const createDefaultTransformer: () => Transformer = () =>
|
|
39
|
+
export const createDefaultTransformer: () => Transformer = () => {
|
|
40
40
|
const scssTransformer = createScssTransformer();
|
|
41
41
|
const lessTransformer = createLessTransformer();
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
return async (source, options) => {
|
|
43
|
+
if (options.from.endsWith('.scss')) {
|
|
44
|
+
return scssTransformer(source, options);
|
|
45
|
+
} else if (options.from.endsWith('.less')) {
|
|
46
|
+
return lessTransformer(source, options);
|
|
47
|
+
}
|
|
48
|
+
// TODO: support postcss
|
|
49
|
+
return false;
|
|
50
|
+
};
|
|
49
51
|
};
|
|
@@ -2,7 +2,7 @@ import type { Transformer } from '../index.js';
|
|
|
2
2
|
import type { TransformerOptions } from './index.js';
|
|
3
3
|
import { handleImportError } from './index.js';
|
|
4
4
|
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
6
6
|
function createLessPluginResolver(Less: typeof import('less'), options: TransformerOptions): Less.Plugin {
|
|
7
7
|
class ResolverFileManager extends Less.FileManager {
|
|
8
8
|
options: TransformerOptions;
|
|
@@ -42,7 +42,6 @@ function createLessPluginResolver(Less: typeof import('less'), options: Transfor
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export const createLessTransformer: () => Transformer = () => {
|
|
45
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
46
45
|
let less: typeof import('less');
|
|
47
46
|
return async (source, options) => {
|
|
48
47
|
less ??= (await import('less').catch(handleImportError('less'))).default;
|
|
@@ -7,48 +7,6 @@ import type { LegacyResult } from 'sass';
|
|
|
7
7
|
import type { Transformer, TransformerOptions } from './index.js';
|
|
8
8
|
import { handleImportError } from './index.js';
|
|
9
9
|
|
|
10
|
-
// const IS_JEST_ENVIRONMENT = process.env.JEST_WORKER_ID !== undefined;
|
|
11
|
-
|
|
12
|
-
// function verifyJestEnvironment() {
|
|
13
|
-
// if (
|
|
14
|
-
// !(
|
|
15
|
-
// 'window' in global &&
|
|
16
|
-
// 'location' in global &&
|
|
17
|
-
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
-
// 'href' in (global as any).location &&
|
|
19
|
-
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
-
// typeof (global as any).location.href === 'string' &&
|
|
21
|
-
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
-
// (global as any).location.href.startsWith('http://')
|
|
23
|
-
// )
|
|
24
|
-
// ) {
|
|
25
|
-
// throw new Error(
|
|
26
|
-
// 'To use dart-sass with jest, dummy `global.window` and `global.location.href` must be set. See https://github.com/sass/dart-sass/issues/1692#issuecomment-1229219993 .',
|
|
27
|
-
// );
|
|
28
|
-
// }
|
|
29
|
-
// }
|
|
30
|
-
|
|
31
|
-
// const createImporterForJest: (from: string) => Importer<'async'> = (from) => ({
|
|
32
|
-
// canonicalize(url) {
|
|
33
|
-
// // NOTE: The format of `url` changes depending on the import source.
|
|
34
|
-
// //
|
|
35
|
-
// // - When `from === '/test/1.scss'` and `@import './2.scss'` in `/test/1.scss` is resolved, `url === '2.scss'`.
|
|
36
|
-
// // - When `from === '/test/1.scss'` and `@import './3.scss'` in `/test/2.scss` is resolved, `url === 'file:///test/3.scss'`.
|
|
37
|
-
// //
|
|
38
|
-
// // That is, the paths of @import statements written to the `from` file is passed through unresolved,
|
|
39
|
-
// // but paths written to other files is passed through resolved to absolute paths.
|
|
40
|
-
// return new URL(url, pathToFileURL(from));
|
|
41
|
-
// },
|
|
42
|
-
// async load(canonicalUrl) {
|
|
43
|
-
// return {
|
|
44
|
-
// contents: await readFile(fileURLToPath(canonicalUrl.href), 'utf8'),
|
|
45
|
-
// syntax: 'scss',
|
|
46
|
-
// sourceMapUrl: canonicalUrl,
|
|
47
|
-
// };
|
|
48
|
-
// },
|
|
49
|
-
// });
|
|
50
|
-
|
|
51
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
52
10
|
async function renderSass(sass: typeof import('sass'), source: string, options: TransformerOptions) {
|
|
53
11
|
return new Promise<LegacyResult>((resolve, reject) => {
|
|
54
12
|
sass.render(
|
|
@@ -76,19 +34,10 @@ async function renderSass(sass: typeof import('sass'), source: string, options:
|
|
|
76
34
|
}
|
|
77
35
|
|
|
78
36
|
export const createScssTransformer: () => Transformer = () => {
|
|
79
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
80
37
|
let sass: typeof import('sass');
|
|
81
38
|
return async (source, options) => {
|
|
82
39
|
sass ??= (await import('sass').catch(handleImportError('sass'))).default;
|
|
83
40
|
const result = await renderSass(sass, source, options);
|
|
84
41
|
return { css: result.css.toString(), map: result.map!.toString(), dependencies: result.stats.includedFiles };
|
|
85
|
-
|
|
86
|
-
// if (IS_JEST_ENVIRONMENT) verifyJestEnvironment();
|
|
87
|
-
// const result = await sass.default.compileStringAsync(source, {
|
|
88
|
-
// url: pathToFileURL(from),
|
|
89
|
-
// sourceMap: true,
|
|
90
|
-
// importers: IS_JEST_ENVIRONMENT ? [createImporterForJest(from)] : [],
|
|
91
|
-
// });
|
|
92
|
-
// return { css: result.css, map: result.sourceMap!, dependencies: result.loadedUrls };
|
|
93
42
|
};
|
|
94
43
|
};
|