happy-css-modules 0.4.0 → 0.6.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 +172 -53
- package/dist/cli.js +45 -11
- package/dist/cli.js.map +1 -1
- package/dist/cli.test.js +20 -4
- package/dist/cli.test.js.map +1 -1
- package/dist/emitter/dts.d.ts +3 -4
- package/dist/emitter/dts.js +13 -15
- package/dist/emitter/dts.js.map +1 -1
- package/dist/emitter/dts.test.js +7 -10
- package/dist/emitter/dts.test.js.map +1 -1
- package/dist/emitter/index.d.ts +6 -15
- package/dist/emitter/index.js +18 -13
- package/dist/emitter/index.js.map +1 -1
- package/dist/emitter/index.test.js +0 -50
- package/dist/emitter/index.test.js.map +1 -1
- package/dist/emitter/source-map.d.ts +1 -3
- package/dist/emitter/source-map.js +2 -3
- package/dist/emitter/source-map.js.map +1 -1
- package/dist/emitter/source-map.test.js +1 -4
- package/dist/emitter/source-map.test.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/integration-test/go-to-definition.test.js +1 -0
- package/dist/integration-test/go-to-definition.test.js.map +1 -1
- package/dist/{loader → locator}/index.d.ts +6 -4
- package/dist/{loader → locator}/index.js +13 -5
- package/dist/locator/index.js.map +1 -0
- package/dist/{loader → locator}/index.test.d.ts +0 -0
- package/dist/{loader → locator}/index.test.js +100 -14
- package/dist/locator/index.test.js.map +1 -0
- package/dist/{loader → locator}/postcss.d.ts +5 -1
- package/dist/{loader → locator}/postcss.js +26 -30
- package/dist/{loader → locator}/postcss.js.map +1 -1
- package/dist/{loader → locator}/postcss.test.d.ts +0 -0
- package/dist/{loader → locator}/postcss.test.js +0 -0
- package/dist/locator/postcss.test.js.map +1 -0
- package/dist/resolver/webpack-resolver.d.ts +12 -2
- package/dist/resolver/webpack-resolver.js +12 -3
- package/dist/resolver/webpack-resolver.js.map +1 -1
- package/dist/resolver/webpack-resolver.test.js +43 -7
- package/dist/resolver/webpack-resolver.test.js.map +1 -1
- package/dist/runner.d.ts +23 -1
- package/dist/runner.js +62 -23
- package/dist/runner.js.map +1 -1
- package/dist/runner.test.js +90 -11
- package/dist/runner.test.js.map +1 -1
- package/dist/test/util.d.ts +2 -2
- package/dist/test/util.js +16 -9
- package/dist/test/util.js.map +1 -1
- package/dist/transformer/index.d.ts +4 -2
- package/dist/transformer/index.js +7 -3
- package/dist/transformer/index.js.map +1 -1
- package/dist/transformer/index.test.d.ts +1 -0
- package/dist/transformer/index.test.js +66 -0
- package/dist/transformer/index.test.js.map +1 -0
- package/dist/transformer/less-transformer.test.js +14 -14
- package/dist/transformer/less-transformer.test.js.map +1 -1
- package/dist/transformer/postcss-transformer.d.ts +12 -0
- package/dist/transformer/postcss-transformer.js +32 -0
- package/dist/transformer/postcss-transformer.js.map +1 -0
- package/dist/transformer/postcss-transformer.test.d.ts +1 -0
- package/dist/transformer/postcss-transformer.test.js +176 -0
- package/dist/transformer/postcss-transformer.test.js.map +1 -0
- package/dist/transformer/scss-transformer.js +19 -17
- package/dist/transformer/scss-transformer.js.map +1 -1
- package/dist/transformer/scss-transformer.test.js +16 -16
- package/dist/transformer/scss-transformer.test.js.map +1 -1
- package/dist/util.d.ts +2 -0
- package/dist/util.js +19 -2
- package/dist/util.js.map +1 -1
- package/package.json +10 -9
- package/src/cli.test.ts +24 -4
- package/src/cli.ts +44 -12
- package/src/emitter/dts.test.ts +7 -12
- package/src/emitter/dts.ts +15 -15
- package/src/emitter/index.test.ts +0 -52
- package/src/emitter/index.ts +22 -29
- package/src/emitter/source-map.test.ts +1 -6
- package/src/emitter/source-map.ts +3 -4
- package/src/index.ts +9 -2
- package/src/integration-test/go-to-definition.test.ts +1 -0
- package/src/{loader → locator}/index.test.ts +101 -14
- package/src/{loader → locator}/index.ts +16 -8
- package/src/{loader → locator}/postcss.test.ts +0 -0
- package/src/{loader → locator}/postcss.ts +42 -40
- package/src/resolver/webpack-resolver.test.ts +63 -7
- package/src/resolver/webpack-resolver.ts +26 -5
- package/src/runner.test.ts +103 -11
- package/src/runner.ts +83 -25
- package/src/test/util.ts +16 -10
- package/src/transformer/index.test.ts +71 -0
- package/src/transformer/index.ts +12 -4
- package/src/transformer/less-transformer.test.ts +14 -14
- package/src/transformer/postcss-transformer.test.ts +188 -0
- package/src/transformer/postcss-transformer.ts +57 -0
- package/src/transformer/scss-transformer.test.ts +16 -16
- package/src/transformer/scss-transformer.ts +25 -27
- package/src/util.ts +21 -2
- package/dist/loader/index.js.map +0 -1
- package/dist/loader/index.test.js.map +0 -1
- package/dist/loader/postcss.test.js.map +0 -1
package/src/emitter/dts.test.ts
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import dedent from 'dedent';
|
|
2
2
|
import { SourceMapConsumer } from 'source-map';
|
|
3
|
-
import {
|
|
3
|
+
import { Locator } from '../locator/index.js';
|
|
4
4
|
import { getFixturePath, createFixtures } from '../test/util.js';
|
|
5
5
|
import { generateDtsContentWithSourceMap, getDtsFilePath } from './dts.js';
|
|
6
6
|
import { type DtsFormatOptions } from './index.js';
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const locator = new Locator();
|
|
9
9
|
const isExternalFile = () => false;
|
|
10
10
|
|
|
11
11
|
test('getDtsFilePath', () => {
|
|
12
|
-
expect(getDtsFilePath('/app/src/dir/1.css'
|
|
13
|
-
expect(getDtsFilePath('/app/src/dir/1.css', { rootDir: '/app', outDir: '/app/dist' })).toBe(
|
|
14
|
-
'/app/dist/src/dir/1.css.d.ts',
|
|
15
|
-
);
|
|
16
|
-
expect(() => getDtsFilePath('/tmp/src/dir/1.css', { rootDir: '/app', outDir: '/app/dist' })).toThrow();
|
|
17
|
-
expect(() => getDtsFilePath('/app/src/dir/1.css', { rootDir: '/app', outDir: '/tmp/dist' })).toThrow();
|
|
12
|
+
expect(getDtsFilePath('/app/src/dir/1.css')).toBe('/app/src/dir/1.css.d.ts');
|
|
18
13
|
});
|
|
19
14
|
|
|
20
15
|
describe('generateDtsContentWithSourceMap', () => {
|
|
@@ -40,7 +35,7 @@ describe('generateDtsContentWithSourceMap', () => {
|
|
|
40
35
|
.d {}
|
|
41
36
|
`,
|
|
42
37
|
});
|
|
43
|
-
const result = await
|
|
38
|
+
const result = await locator.load(filePath);
|
|
44
39
|
const { dtsContent, sourceMap } = generateDtsContentWithSourceMap(
|
|
45
40
|
filePath,
|
|
46
41
|
dtsFilePath,
|
|
@@ -94,7 +89,7 @@ describe('generateDtsContentWithSourceMap', () => {
|
|
|
94
89
|
.foo_bar {}
|
|
95
90
|
`,
|
|
96
91
|
});
|
|
97
|
-
return await
|
|
92
|
+
return await locator.load(filePath);
|
|
98
93
|
}
|
|
99
94
|
test('undefined', async () => {
|
|
100
95
|
const result = await getResult(filePath);
|
|
@@ -215,7 +210,7 @@ describe('generateDtsContentWithSourceMap', () => {
|
|
|
215
210
|
createFixtures({
|
|
216
211
|
'/test/1.css': `.a {}`,
|
|
217
212
|
});
|
|
218
|
-
const result = await
|
|
213
|
+
const result = await locator.load(filePath);
|
|
219
214
|
const { dtsContent, sourceMap } = generateDtsContentWithSourceMap(
|
|
220
215
|
getFixturePath('/test/src/1.css'),
|
|
221
216
|
getFixturePath('/test/dist/1.css.d.ts'),
|
|
@@ -244,7 +239,7 @@ describe('generateDtsContentWithSourceMap', () => {
|
|
|
244
239
|
'/test/2.css': `.b {}`,
|
|
245
240
|
'/test/3.css': `.c {}`,
|
|
246
241
|
});
|
|
247
|
-
const result = await
|
|
242
|
+
const result = await locator.load(filePath);
|
|
248
243
|
const { dtsContent } = generateDtsContentWithSourceMap(
|
|
249
244
|
filePath,
|
|
250
245
|
dtsFilePath,
|
package/src/emitter/dts.ts
CHANGED
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
import { EOL } from 'os';
|
|
2
|
-
import {
|
|
2
|
+
import { basename } from 'path';
|
|
3
3
|
import camelcase from 'camelcase';
|
|
4
4
|
import { SourceNode, type CodeWithSourceMap } from '../library/source-map/index.js';
|
|
5
|
-
import { type Token } from '../
|
|
5
|
+
import { type Token } from '../locator/index.js';
|
|
6
6
|
import { type LocalsConvention } from '../runner.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getRelativePath, type DtsFormatOptions } from './index.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Get .d.ts file path.
|
|
11
11
|
* @param filePath The path to the source file (i.e. `/dir/foo.css`). It is absolute.
|
|
12
|
-
* @param distOptions The distribution option.
|
|
13
12
|
* @returns The path to the .d.ts file. It is absolute.
|
|
14
13
|
*/
|
|
15
|
-
export function getDtsFilePath(filePath: string
|
|
16
|
-
|
|
17
|
-
if (!isSubDirectoryFile(distOptions.rootDir, filePath))
|
|
18
|
-
throw new Error(`The filePath(${filePath}) is not a subdirectory of rootDir(${distOptions.rootDir}).`);
|
|
19
|
-
if (!isSubDirectoryFile(distOptions.rootDir, distOptions.outDir))
|
|
20
|
-
throw new Error(`The outDir(${distOptions.outDir}) is not a subdirectory of rootDir(${distOptions.rootDir}).`);
|
|
21
|
-
return join(distOptions.outDir, relative(distOptions.rootDir, filePath) + '.d.ts');
|
|
22
|
-
} else {
|
|
23
|
-
return filePath + '.d.ts';
|
|
24
|
-
}
|
|
14
|
+
export function getDtsFilePath(filePath: string): string {
|
|
15
|
+
return filePath + '.d.ts';
|
|
25
16
|
}
|
|
26
17
|
|
|
27
18
|
function dashesCamelCase(str: string): string {
|
|
@@ -65,7 +56,16 @@ function generateTokenDeclarations(
|
|
|
65
56
|
// This is due to the sourcemap specification. Therefore, we output multiple type definitions
|
|
66
57
|
// with the same name and assign a separate original position to each.
|
|
67
58
|
|
|
68
|
-
for (
|
|
59
|
+
for (let originalLocation of token.originalLocations) {
|
|
60
|
+
if (originalLocation.filePath === undefined) {
|
|
61
|
+
// If the original location is not specified, fallback to the source file.
|
|
62
|
+
originalLocation = {
|
|
63
|
+
filePath: filePath,
|
|
64
|
+
start: { line: 1, column: 1 },
|
|
65
|
+
end: { line: 1, column: 1 },
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
69
|
result.push(
|
|
70
70
|
originalLocation.filePath === filePath || isExternalFile(originalLocation.filePath)
|
|
71
71
|
? new SourceNode(null, null, null, [
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { readFile, stat } from 'fs/promises';
|
|
2
2
|
import { jest } from '@jest/globals';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
3
|
import { createFixtures, exists, fakeToken, getFixturePath, waitForAsyncTask } from '../test/util.js';
|
|
5
4
|
import { emitGeneratedFiles, getRelativePath, isSubDirectoryFile } from './index.js';
|
|
6
5
|
|
|
@@ -27,10 +26,8 @@ describe('emitGeneratedFiles', () => {
|
|
|
27
26
|
const defaultArgs = {
|
|
28
27
|
filePath: getFixturePath('/test/1.css'),
|
|
29
28
|
tokens: [fakeToken({ name: 'foo', originalLocations: [{ start: { line: 1, column: 1 } }] })],
|
|
30
|
-
distOptions: undefined,
|
|
31
29
|
emitDeclarationMap: true,
|
|
32
30
|
dtsFormatOptions: undefined,
|
|
33
|
-
silent: true,
|
|
34
31
|
cwd: getFixturePath('/test'),
|
|
35
32
|
isExternalFile: () => false,
|
|
36
33
|
};
|
|
@@ -78,53 +75,4 @@ describe('emitGeneratedFiles', () => {
|
|
|
78
75
|
expect(mtimeForDts1).not.toEqual(mtimeForDts3); // not skipped
|
|
79
76
|
expect(mtimeForSourceMap1).not.toEqual(mtimeForSourceMap3); // not skipped
|
|
80
77
|
});
|
|
81
|
-
test('outputs write log', async () => {
|
|
82
|
-
await emitGeneratedFiles({
|
|
83
|
-
...defaultArgs,
|
|
84
|
-
filePath: getFixturePath('/test/1.css'),
|
|
85
|
-
emitDeclarationMap: true,
|
|
86
|
-
silent: false,
|
|
87
|
-
});
|
|
88
|
-
expect(consoleLogSpy).toHaveBeenCalledTimes(2);
|
|
89
|
-
expect(consoleLogSpy).toHaveBeenNthCalledWith(1, `Wrote ${chalk.green('1.css.d.ts')}`);
|
|
90
|
-
expect(consoleLogSpy).toHaveBeenNthCalledWith(2, `Wrote ${chalk.green('1.css.d.ts.map')}`);
|
|
91
|
-
consoleLogSpy.mockClear();
|
|
92
|
-
|
|
93
|
-
await emitGeneratedFiles({
|
|
94
|
-
...defaultArgs,
|
|
95
|
-
filePath: getFixturePath('/test/2.css'),
|
|
96
|
-
emitDeclarationMap: false,
|
|
97
|
-
silent: false,
|
|
98
|
-
});
|
|
99
|
-
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
|
100
|
-
expect(consoleLogSpy).toHaveBeenNthCalledWith(1, `Wrote ${chalk.green('2.css.d.ts')}`);
|
|
101
|
-
consoleLogSpy.mockClear();
|
|
102
|
-
|
|
103
|
-
await emitGeneratedFiles({
|
|
104
|
-
...defaultArgs,
|
|
105
|
-
filePath: getFixturePath('/test/3.css'),
|
|
106
|
-
emitDeclarationMap: false,
|
|
107
|
-
silent: true,
|
|
108
|
-
});
|
|
109
|
-
expect(consoleLogSpy).toHaveBeenCalledTimes(0);
|
|
110
|
-
});
|
|
111
|
-
test('changes working directory by cwd', async () => {
|
|
112
|
-
await emitGeneratedFiles({
|
|
113
|
-
...defaultArgs,
|
|
114
|
-
filePath: getFixturePath('/test/1.css'),
|
|
115
|
-
emitDeclarationMap: false,
|
|
116
|
-
silent: false,
|
|
117
|
-
cwd: getFixturePath('/test'),
|
|
118
|
-
});
|
|
119
|
-
await emitGeneratedFiles({
|
|
120
|
-
...defaultArgs,
|
|
121
|
-
filePath: getFixturePath('/test/1.css'),
|
|
122
|
-
emitDeclarationMap: false,
|
|
123
|
-
silent: false,
|
|
124
|
-
cwd: getFixturePath('/'),
|
|
125
|
-
});
|
|
126
|
-
expect(consoleLogSpy).toHaveBeenCalledTimes(2);
|
|
127
|
-
expect(consoleLogSpy).toHaveBeenNthCalledWith(1, `Wrote ${chalk.green('1.css.d.ts')}`);
|
|
128
|
-
expect(consoleLogSpy).toHaveBeenNthCalledWith(2, `Wrote ${chalk.green('test/1.css.d.ts')}`);
|
|
129
|
-
});
|
|
130
78
|
});
|
package/src/emitter/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { dirname, isAbsolute, relative } from 'path';
|
|
2
|
-
import
|
|
3
|
-
import { type Token } from '../loader/index.js';
|
|
2
|
+
import { type Token } from '../locator/index.js';
|
|
4
3
|
import { type LocalsConvention } from '../runner.js';
|
|
4
|
+
import { exists } from '../util.js';
|
|
5
5
|
import { generateDtsContentWithSourceMap, getDtsFilePath } from './dts.js';
|
|
6
6
|
import { writeFileIfChanged } from './file-system.js';
|
|
7
7
|
import { generateSourceMappingURLComment, getSourceMapFilePath } from './source-map.js';
|
|
@@ -19,18 +19,6 @@ export function isSubDirectoryFile(fromDirectory: string, toFilePath: string): b
|
|
|
19
19
|
return isAbsolute(toFilePath) && toFilePath.startsWith(fromDirectory);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function outputWriteLog(cwd: string, filePath: string) {
|
|
23
|
-
console.log('Wrote ' + chalk.green(relative(cwd, filePath)));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** The distribution option. */
|
|
27
|
-
export type DistOptions = {
|
|
28
|
-
/** Root directory. It is absolute. */
|
|
29
|
-
rootDir: string;
|
|
30
|
-
/** The path to the output directory. It is absolute. */
|
|
31
|
-
outDir: string;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
22
|
export type DtsFormatOptions = {
|
|
35
23
|
localsConvention?: LocalsConvention;
|
|
36
24
|
};
|
|
@@ -41,16 +29,10 @@ export type EmitterOptions = {
|
|
|
41
29
|
filePath: string;
|
|
42
30
|
/** The tokens exported by the source file. */
|
|
43
31
|
tokens: Token[];
|
|
44
|
-
/** The distribution option. */
|
|
45
|
-
distOptions: DistOptions | undefined;
|
|
46
32
|
/** Whether to output declaration map (i.e. `/dir/foo.css.d.ts.map`) or not. */
|
|
47
33
|
emitDeclarationMap: boolean | undefined;
|
|
48
34
|
/** The options for formatting the type definition. */
|
|
49
35
|
dtsFormatOptions: DtsFormatOptions | undefined;
|
|
50
|
-
/** Silent output. Do not show "files written" messages */
|
|
51
|
-
silent: boolean;
|
|
52
|
-
/** Working directory path. */
|
|
53
|
-
cwd: string;
|
|
54
36
|
/** Whether the file is from an external library or not. */
|
|
55
37
|
isExternalFile: (filePath: string) => boolean;
|
|
56
38
|
};
|
|
@@ -58,15 +40,12 @@ export type EmitterOptions = {
|
|
|
58
40
|
export async function emitGeneratedFiles({
|
|
59
41
|
filePath,
|
|
60
42
|
tokens,
|
|
61
|
-
distOptions,
|
|
62
43
|
emitDeclarationMap,
|
|
63
44
|
dtsFormatOptions,
|
|
64
|
-
silent,
|
|
65
|
-
cwd,
|
|
66
45
|
isExternalFile,
|
|
67
46
|
}: EmitterOptions): Promise<void> {
|
|
68
|
-
const dtsFilePath = getDtsFilePath(filePath
|
|
69
|
-
const sourceMapFilePath = getSourceMapFilePath(filePath
|
|
47
|
+
const dtsFilePath = getDtsFilePath(filePath);
|
|
48
|
+
const sourceMapFilePath = getSourceMapFilePath(filePath);
|
|
70
49
|
const { dtsContent, sourceMap } = generateDtsContentWithSourceMap(
|
|
71
50
|
filePath,
|
|
72
51
|
dtsFilePath,
|
|
@@ -79,14 +58,28 @@ export async function emitGeneratedFiles({
|
|
|
79
58
|
if (emitDeclarationMap) {
|
|
80
59
|
const sourceMappingURLComment = generateSourceMappingURLComment(dtsFilePath, sourceMapFilePath);
|
|
81
60
|
await writeFileIfChanged(dtsFilePath, dtsContent + sourceMappingURLComment);
|
|
82
|
-
if (!silent) outputWriteLog(cwd, dtsFilePath);
|
|
83
|
-
|
|
84
61
|
// NOTE: tsserver does not support inline declaration maps. Therefore, sourcemap files must be output.
|
|
85
62
|
// blocked by: https://github.com/microsoft/TypeScript/issues/38966
|
|
86
63
|
await writeFileIfChanged(sourceMapFilePath, sourceMap.toString());
|
|
87
|
-
if (!silent) outputWriteLog(cwd, sourceMapFilePath);
|
|
88
64
|
} else {
|
|
89
65
|
await writeFileIfChanged(dtsFilePath, dtsContent);
|
|
90
|
-
if (!silent) outputWriteLog(cwd, dtsFilePath);
|
|
91
66
|
}
|
|
92
67
|
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns true if .d.ts (and .d.ts.map) files are generated for the given file.
|
|
71
|
+
*/
|
|
72
|
+
export async function isGeneratedFilesExist(
|
|
73
|
+
filePath: string,
|
|
74
|
+
emitDeclarationMap: boolean | undefined,
|
|
75
|
+
): Promise<boolean> {
|
|
76
|
+
const dtsFilePath = getDtsFilePath(filePath);
|
|
77
|
+
const sourceMapFilePath = getSourceMapFilePath(filePath);
|
|
78
|
+
if (emitDeclarationMap && !(await exists(sourceMapFilePath))) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
if (!(await exists(dtsFilePath))) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
@@ -2,12 +2,7 @@ import { EOL } from 'os';
|
|
|
2
2
|
import { getSourceMapFilePath, generateSourceMappingURLComment } from './source-map.js';
|
|
3
3
|
|
|
4
4
|
test('getSourceMapFilePath', () => {
|
|
5
|
-
expect(getSourceMapFilePath('/app/src/dir/1.css'
|
|
6
|
-
expect(getSourceMapFilePath('/app/src/dir/1.css', { rootDir: '/app', outDir: '/app/dist' })).toBe(
|
|
7
|
-
'/app/dist/src/dir/1.css.d.ts.map',
|
|
8
|
-
);
|
|
9
|
-
expect(() => getSourceMapFilePath('/tmp/src/dir/1.css', { rootDir: '/app', outDir: '/app/dist' })).toThrow();
|
|
10
|
-
expect(() => getSourceMapFilePath('/app/src/dir/1.css', { rootDir: '/app', outDir: '/tmp/dist' })).toThrow();
|
|
5
|
+
expect(getSourceMapFilePath('/app/src/dir/1.css')).toBe('/app/src/dir/1.css.d.ts.map');
|
|
11
6
|
});
|
|
12
7
|
|
|
13
8
|
test('generateSourceMappingURLComment', () => {
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { EOL } from 'os';
|
|
2
2
|
import { getDtsFilePath } from './dts.js';
|
|
3
|
-
import {
|
|
3
|
+
import { getRelativePath } from './index.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Get .d.ts.map file path.
|
|
7
7
|
* @param filePath The path to the source file (i.e. `foo.css`). It is absolute.
|
|
8
|
-
* @param distOptions The distribution option.
|
|
9
8
|
* @returns The path to the .d.ts.map file. It is absolute.
|
|
10
9
|
*/
|
|
11
|
-
export function getSourceMapFilePath(filePath: string
|
|
12
|
-
return getDtsFilePath(filePath
|
|
10
|
+
export function getSourceMapFilePath(filePath: string): string {
|
|
11
|
+
return getDtsFilePath(filePath) + '.map';
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
export function generateSourceMappingURLComment(dtsFilePath: string, sourceMapFilePath: string): string {
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
export { parseArgv } from './cli.js';
|
|
2
|
-
export { run } from './runner.js';
|
|
3
|
-
export {
|
|
2
|
+
export { run, type LocalsConvention } from './runner.js';
|
|
3
|
+
export {
|
|
4
|
+
type Transformer,
|
|
5
|
+
type TransformerOptions,
|
|
6
|
+
type TransformResult,
|
|
7
|
+
createDefaultTransformer,
|
|
8
|
+
} from './transformer/index.js';
|
|
9
|
+
export { type Resolver, type ResolverOptions, createDefaultResolver } from './resolver/index.js';
|
|
10
|
+
export { Locator, type LocatorOptions, type LoadResult, type Token, type Location } from './locator/index.js';
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs, { readFile, writeFile } from 'fs/promises';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { jest } from '@jest/globals';
|
|
3
4
|
import dedent from 'dedent';
|
|
5
|
+
import { createDefaultTransformer } from '../index.js';
|
|
4
6
|
import { createFixtures, FIXTURE_DIR_PATH, getFixturePath } from '../test/util.js';
|
|
5
7
|
import { sleepSync } from '../util.js';
|
|
6
8
|
|
|
@@ -13,10 +15,10 @@ jest.unstable_mockModule('fs/promises', () => ({
|
|
|
13
15
|
|
|
14
16
|
// After the mock of fs/promises is complete, . /index.js after the mock of fs/promises is complete.
|
|
15
17
|
// ref: https://www.coolcomputerclub.com/posts/jest-hoist-await/
|
|
16
|
-
const {
|
|
18
|
+
const { Locator } = await import('./index.js');
|
|
17
19
|
// NOTE: ../test/util.js depends on . /index.js, so it must also be imported dynamically...
|
|
18
20
|
|
|
19
|
-
const
|
|
21
|
+
const locator = new Locator();
|
|
20
22
|
|
|
21
23
|
afterEach(() => {
|
|
22
24
|
readFileSpy.mockClear();
|
|
@@ -29,7 +31,7 @@ test('basic', async () => {
|
|
|
29
31
|
.b {}
|
|
30
32
|
`,
|
|
31
33
|
});
|
|
32
|
-
const result = await
|
|
34
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
33
35
|
expect(result).toMatchInlineSnapshot(`
|
|
34
36
|
{
|
|
35
37
|
dependencies: [],
|
|
@@ -75,7 +77,7 @@ test('tracks other files when `@import` is present', async () => {
|
|
|
75
77
|
.d {}
|
|
76
78
|
`,
|
|
77
79
|
});
|
|
78
|
-
const result = await
|
|
80
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
79
81
|
expect(result).toMatchInlineSnapshot(`
|
|
80
82
|
{
|
|
81
83
|
dependencies: [
|
|
@@ -135,7 +137,7 @@ test('tracks other files when `composes` is present', async () => {
|
|
|
135
137
|
.e {}
|
|
136
138
|
`,
|
|
137
139
|
});
|
|
138
|
-
const result = await
|
|
140
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
139
141
|
expect(result).toMatchInlineSnapshot(`
|
|
140
142
|
{
|
|
141
143
|
dependencies: ["<fixtures>/test/2.css", "<fixtures>/test/3.css", "<fixtures>/test/4.css"],
|
|
@@ -199,7 +201,7 @@ test('normalizes tokens', async () => {
|
|
|
199
201
|
.c {}
|
|
200
202
|
`,
|
|
201
203
|
});
|
|
202
|
-
const result = await
|
|
204
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
203
205
|
expect(result).toMatchInlineSnapshot(`
|
|
204
206
|
{
|
|
205
207
|
dependencies: ["<fixtures>/test/2.css", "<fixtures>/test/3.css"],
|
|
@@ -248,7 +250,7 @@ test.failing('returns the result from the cache when the file has not been modif
|
|
|
248
250
|
.d {}
|
|
249
251
|
`,
|
|
250
252
|
});
|
|
251
|
-
await
|
|
253
|
+
await locator.load(getFixturePath('/test/1.css'));
|
|
252
254
|
expect(readFileSpy).toHaveBeenCalledTimes(3);
|
|
253
255
|
expect(readFileSpy).toHaveBeenNthCalledWith(1, '/test/1.css', 'utf-8');
|
|
254
256
|
expect(readFileSpy).toHaveBeenNthCalledWith(2, '/test/2.css', 'utf-8');
|
|
@@ -260,17 +262,17 @@ test.failing('returns the result from the cache when the file has not been modif
|
|
|
260
262
|
await writeFile(getFixturePath('/test/2.css'), await readFile(getFixturePath('/test/2.css'), 'utf-8'));
|
|
261
263
|
|
|
262
264
|
// `3.css` is not updated, so the cache is used. Therefore, `readFile` is not called.
|
|
263
|
-
await
|
|
265
|
+
await locator.load(getFixturePath('/test/3.css'));
|
|
264
266
|
expect(readFileSpy).toHaveBeenCalledTimes(0);
|
|
265
267
|
|
|
266
268
|
// `1.css` is not updated, but dependencies are updated, so the cache is used. Therefore, `readFile` is called.
|
|
267
|
-
await
|
|
269
|
+
await locator.load(getFixturePath('/test/1.css'));
|
|
268
270
|
expect(readFileSpy).toHaveBeenCalledTimes(2);
|
|
269
271
|
expect(readFileSpy).toHaveBeenNthCalledWith(1, '/test/1.css', 'utf-8');
|
|
270
272
|
expect(readFileSpy).toHaveBeenNthCalledWith(2, '/test/2.css', 'utf-8');
|
|
271
273
|
|
|
272
274
|
// ``2.css` is updated, but the cache is already available because it was updated in the previous step. Therefore, `readFile` is not called.
|
|
273
|
-
await
|
|
275
|
+
await locator.load(getFixturePath('/test/2.css'));
|
|
274
276
|
expect(readFileSpy).toHaveBeenCalledTimes(2);
|
|
275
277
|
});
|
|
276
278
|
|
|
@@ -289,7 +291,7 @@ test('ignores the composition of non-existent tokens', async () => {
|
|
|
289
291
|
.b {}
|
|
290
292
|
`,
|
|
291
293
|
});
|
|
292
|
-
const result = await
|
|
294
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
293
295
|
expect(result.tokens.map((t) => t.name)).toStrictEqual(['a', 'b']);
|
|
294
296
|
});
|
|
295
297
|
|
|
@@ -304,14 +306,90 @@ test('throws error the composition of non-existent file', async () => {
|
|
|
304
306
|
`,
|
|
305
307
|
});
|
|
306
308
|
await expect(async () => {
|
|
307
|
-
await
|
|
309
|
+
await locator.load(getFixturePath('/test/1.css')).catch((e) => {
|
|
308
310
|
e.message = e.message.replace(FIXTURE_DIR_PATH, '<fixtures>');
|
|
309
311
|
throw e;
|
|
310
312
|
});
|
|
311
313
|
}).rejects.toThrowError(`Could not resolve './2.css' in '<fixtures>/test/1.css'`);
|
|
312
314
|
});
|
|
313
315
|
|
|
314
|
-
|
|
316
|
+
describe('supports sourcemap', () => {
|
|
317
|
+
test('restores original locations from sourcemap', async () => {
|
|
318
|
+
const transformer = createDefaultTransformer();
|
|
319
|
+
const locator = new Locator({ transformer });
|
|
320
|
+
createFixtures({
|
|
321
|
+
'/test/1.scss': dedent`
|
|
322
|
+
.nesting {
|
|
323
|
+
dummy: '';
|
|
324
|
+
.nesting_child {
|
|
325
|
+
dummy: '';
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
`,
|
|
329
|
+
});
|
|
330
|
+
const result = await locator.load(getFixturePath('/test/1.scss'));
|
|
331
|
+
expect(result).toMatchInlineSnapshot(`
|
|
332
|
+
{
|
|
333
|
+
dependencies: [],
|
|
334
|
+
tokens: [
|
|
335
|
+
{
|
|
336
|
+
name: "nesting",
|
|
337
|
+
originalLocations: [
|
|
338
|
+
{ filePath: "<fixtures>/test/1.scss", start: { line: 1, column: 1 }, end: { line: 1, column: 8 } },
|
|
339
|
+
{ filePath: "<fixtures>/test/1.scss", start: { line: 3, column: 3 }, end: { line: 3, column: 10 } },
|
|
340
|
+
],
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "nesting_child",
|
|
344
|
+
originalLocations: [
|
|
345
|
+
{ filePath: "<fixtures>/test/1.scss", start: { line: 3, column: 3 }, end: { line: 3, column: 16 } },
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
}
|
|
350
|
+
`);
|
|
351
|
+
});
|
|
352
|
+
test('treats originalLocation as empty if sourcemap is broken', async () => {
|
|
353
|
+
const uuid = randomUUID();
|
|
354
|
+
createFixtures({
|
|
355
|
+
[`/${uuid}/postcss.config.js`]: dedent`
|
|
356
|
+
module.exports = {
|
|
357
|
+
plugins: [],
|
|
358
|
+
};
|
|
359
|
+
`,
|
|
360
|
+
'/test/1.css': dedent`
|
|
361
|
+
.selector_list_a_1, .selector_list_a_2 {}
|
|
362
|
+
/* In postcss, including newlines in the selector list breaks the sourcemap. */
|
|
363
|
+
.selector_list_b_1,
|
|
364
|
+
.selector_list_b_2 {}
|
|
365
|
+
`,
|
|
366
|
+
});
|
|
367
|
+
const transformer = createDefaultTransformer({ postcssConfig: getFixturePath(`/${uuid}/postcss.config.js`) });
|
|
368
|
+
const locator = new Locator({ transformer });
|
|
369
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
370
|
+
expect(result).toMatchInlineSnapshot(`
|
|
371
|
+
{
|
|
372
|
+
dependencies: [],
|
|
373
|
+
tokens: [
|
|
374
|
+
{
|
|
375
|
+
name: "selector_list_a_1",
|
|
376
|
+
originalLocations: [
|
|
377
|
+
{ filePath: "<fixtures>/test/1.css", start: { line: 1, column: 1 }, end: { line: 1, column: 18 } },
|
|
378
|
+
],
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: "selector_list_a_2",
|
|
382
|
+
originalLocations: [
|
|
383
|
+
{ filePath: "<fixtures>/test/1.css", start: { line: 1, column: 1 }, end: { line: 1, column: 18 } },
|
|
384
|
+
],
|
|
385
|
+
},
|
|
386
|
+
{ name: "selector_list_b_1", originalLocations: [{}] },
|
|
387
|
+
{ name: "selector_list_b_2", originalLocations: [{}] },
|
|
388
|
+
],
|
|
389
|
+
}
|
|
390
|
+
`);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
315
393
|
|
|
316
394
|
test('ignores http(s) protocol file', async () => {
|
|
317
395
|
createFixtures({
|
|
@@ -320,6 +398,15 @@ test('ignores http(s) protocol file', async () => {
|
|
|
320
398
|
@import 'https://example.com/path/https.css';
|
|
321
399
|
`,
|
|
322
400
|
});
|
|
323
|
-
const result = await
|
|
401
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
324
402
|
expect(result.dependencies).toStrictEqual([]);
|
|
325
403
|
});
|
|
404
|
+
|
|
405
|
+
test('block concurrent calls to load method', async () => {
|
|
406
|
+
createFixtures({
|
|
407
|
+
'/test/1.css': `.a {}`,
|
|
408
|
+
});
|
|
409
|
+
await expect(async () => {
|
|
410
|
+
await Promise.all([locator.load(getFixturePath('/test/1.css')), locator.load(getFixturePath('/test/1.css'))]);
|
|
411
|
+
}).rejects.toThrowError('Cannot call `Locator#load` concurrently.');
|
|
412
|
+
});
|
|
@@ -36,7 +36,7 @@ type CacheEntry = {
|
|
|
36
36
|
result: LoadResult;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
/** The result of `
|
|
39
|
+
/** The result of `Locator#load`. */
|
|
40
40
|
export type LoadResult = {
|
|
41
41
|
/** The path of the file imported from the source file with `@import` or `composes`. */
|
|
42
42
|
dependencies: string[];
|
|
@@ -60,7 +60,7 @@ function normalizeTokens(tokens: Token[]): Token[] {
|
|
|
60
60
|
}));
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export type
|
|
63
|
+
export type LocatorOptions = {
|
|
64
64
|
/** The function to transform source code. */
|
|
65
65
|
transformer?: Transformer | undefined;
|
|
66
66
|
/** The function to resolve the path of the imported file. */
|
|
@@ -71,12 +71,13 @@ export type LoaderOptions = {
|
|
|
71
71
|
export type StrictlyResolver = (...args: Parameters<Resolver>) => Promise<string>;
|
|
72
72
|
|
|
73
73
|
/** This class collects information on tokens exported from CSS Modules files. */
|
|
74
|
-
export class
|
|
74
|
+
export class Locator {
|
|
75
75
|
private readonly cache: Map<string, CacheEntry> = new Map();
|
|
76
76
|
private readonly transformer: Transformer | undefined;
|
|
77
77
|
private readonly resolver: StrictlyResolver;
|
|
78
|
+
private loading = false;
|
|
78
79
|
|
|
79
|
-
constructor(options?:
|
|
80
|
+
constructor(options?: LocatorOptions) {
|
|
80
81
|
this.transformer = options?.transformer ?? createDefaultTransformer();
|
|
81
82
|
this.resolver = async (specifier, resolverOptions) => {
|
|
82
83
|
const resolver = options?.resolver ?? createDefaultResolver();
|
|
@@ -137,8 +138,15 @@ export class Loader {
|
|
|
137
138
|
|
|
138
139
|
/** Returns information about the tokens exported from the CSS Modules file. */
|
|
139
140
|
async load(filePath: string): Promise<LoadResult> {
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
if (this.loading) throw new Error('Cannot call `Locator#load` concurrently.');
|
|
142
|
+
this.loading = true;
|
|
143
|
+
const result = await this._load(filePath).finally(() => {
|
|
144
|
+
this.loading = false;
|
|
145
|
+
});
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private async _load(filePath: string): Promise<LoadResult> {
|
|
142
150
|
if (!(await this.isCacheOutdated(filePath))) {
|
|
143
151
|
const cacheEntry = this.cache.get(filePath)!;
|
|
144
152
|
return cacheEntry.result;
|
|
@@ -164,7 +172,7 @@ export class Loader {
|
|
|
164
172
|
if (!importedSheetPath) continue;
|
|
165
173
|
if (isIgnoredSpecifier(importedSheetPath)) continue;
|
|
166
174
|
const from = await this.resolver(importedSheetPath, { request: filePath });
|
|
167
|
-
const result = await this.
|
|
175
|
+
const result = await this._load(from);
|
|
168
176
|
const externalTokens = result.tokens;
|
|
169
177
|
dependencies.push(from, ...result.dependencies);
|
|
170
178
|
tokens.push(...externalTokens);
|
|
@@ -190,7 +198,7 @@ export class Loader {
|
|
|
190
198
|
if (!declarationDetail) continue;
|
|
191
199
|
if (isIgnoredSpecifier(declarationDetail.from)) continue;
|
|
192
200
|
const from = await this.resolver(declarationDetail.from, { request: filePath });
|
|
193
|
-
const result = await this.
|
|
201
|
+
const result = await this._load(from);
|
|
194
202
|
const externalTokens = result.tokens.filter((token) => declarationDetail.tokenNames.includes(token.name));
|
|
195
203
|
dependencies.push(from, ...result.dependencies);
|
|
196
204
|
tokens.push(...externalTokens);
|
|
File without changes
|