happy-css-modules 0.5.0 → 0.6.1
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 +112 -10
- package/dist/cli.js +17 -10
- package/dist/cli.js.map +1 -1
- package/dist/cli.test.js +9 -4
- package/dist/cli.test.js.map +1 -1
- package/dist/emitter/dts.d.ts +3 -4
- package/dist/emitter/dts.js +4 -14
- 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 +1 -0
- package/dist/index.js +1 -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 +25 -17
- package/dist/locator/index.test.js.map +1 -0
- package/dist/{loader → locator}/postcss.d.ts +0 -0
- package/dist/{loader → locator}/postcss.js +0 -0
- package/dist/locator/postcss.js.map +1 -0
- 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/runner.d.ts +10 -1
- package/dist/runner.js +52 -18
- package/dist/runner.js.map +1 -1
- package/dist/runner.test.js +60 -3
- package/dist/runner.test.js.map +1 -1
- package/dist/test/util.d.ts +1 -1
- package/dist/test/util.js +1 -1
- package/dist/test/util.js.map +1 -1
- package/dist/transformer/index.d.ts +1 -1
- package/dist/transformer/index.test.js +8 -8
- package/dist/transformer/index.test.js.map +1 -1
- package/dist/transformer/less-transformer.test.js +8 -8
- package/dist/transformer/less-transformer.test.js.map +1 -1
- package/dist/transformer/postcss-transformer.test.js +13 -13
- package/dist/transformer/postcss-transformer.test.js.map +1 -1
- package/dist/transformer/scss-transformer.test.js +8 -8
- 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 +6 -7
- package/src/cli.test.ts +9 -4
- package/src/cli.ts +17 -11
- package/src/emitter/dts.test.ts +7 -12
- package/src/emitter/dts.ts +5 -14
- 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 +1 -0
- package/src/integration-test/go-to-definition.test.ts +1 -0
- package/src/{loader → locator}/index.test.ts +26 -17
- package/src/{loader → locator}/index.ts +16 -8
- package/src/{loader → locator}/postcss.test.ts +0 -0
- package/src/{loader → locator}/postcss.ts +0 -0
- package/src/runner.test.ts +71 -3
- package/src/runner.ts +61 -19
- package/src/test/util.ts +1 -1
- package/src/transformer/index.test.ts +8 -8
- package/src/transformer/index.ts +1 -1
- package/src/transformer/less-transformer.test.ts +8 -8
- package/src/transformer/postcss-transformer.test.ts +13 -13
- package/src/transformer/scss-transformer.test.ts +8 -8
- 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.js.map +0 -1
- package/dist/loader/postcss.test.js.map +0 -1
package/src/cli.test.ts
CHANGED
|
@@ -10,10 +10,6 @@ describe('parseArgv', () => {
|
|
|
10
10
|
// TODO: Support multiple patterns
|
|
11
11
|
// parseArgv([...baseArgs, 'foo', 'bar']);
|
|
12
12
|
});
|
|
13
|
-
test('--outDir', () => {
|
|
14
|
-
expect(parseArgv([...baseArgs, '1.css', '--outDir', 'foo']).outDir).toStrictEqual('foo');
|
|
15
|
-
expect(parseArgv([...baseArgs, '1.css', '--outDir', '1']).outDir).toStrictEqual('1');
|
|
16
|
-
});
|
|
17
13
|
test('--watch', () => {
|
|
18
14
|
expect(parseArgv([...baseArgs, '1.css', '--watch']).watch).toBe(true);
|
|
19
15
|
expect(parseArgv([...baseArgs, '1.css', '--no-watch']).watch).toBe(false);
|
|
@@ -60,6 +56,15 @@ describe('parseArgv', () => {
|
|
|
60
56
|
'postcss.config.js',
|
|
61
57
|
);
|
|
62
58
|
});
|
|
59
|
+
test('--cache', () => {
|
|
60
|
+
expect(parseArgv([...baseArgs, '1.css']).cache).toBe(true);
|
|
61
|
+
expect(parseArgv([...baseArgs, '1.css', '--cache']).cache).toBe(true);
|
|
62
|
+
expect(parseArgv([...baseArgs, '1.css', '--no-cache']).cache).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
test('--cacheStrategy', () => {
|
|
65
|
+
expect(parseArgv([...baseArgs, '1.css']).cacheStrategy).toBe('content');
|
|
66
|
+
expect(parseArgv([...baseArgs, '1.css', '--cacheStrategy', 'metadata']).cacheStrategy).toBe('metadata');
|
|
67
|
+
});
|
|
63
68
|
test('--silent', () => {
|
|
64
69
|
expect(parseArgv([...baseArgs, '1.css', '--silent']).silent).toBe(true);
|
|
65
70
|
expect(parseArgv([...baseArgs, '1.css', '--no-silent']).silent).toBe(false);
|
package/src/cli.ts
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { dirname, resolve } from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
1
|
import yargs from 'yargs';
|
|
5
2
|
import { hideBin } from 'yargs/helpers';
|
|
6
3
|
import { type RunnerOptions } from './runner.js';
|
|
7
|
-
|
|
8
|
-
const pkgJson = JSON.parse(readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '../package.json'), 'utf-8'));
|
|
4
|
+
import { getPackageJson } from './util.js';
|
|
9
5
|
|
|
10
6
|
/**
|
|
11
7
|
* Parse command line arguments.
|
|
12
8
|
* @returns Runner options.
|
|
13
9
|
*/
|
|
14
10
|
export function parseArgv(argv: string[]): RunnerOptions {
|
|
11
|
+
const pkgJson = getPackageJson();
|
|
15
12
|
const parsedArgv = yargs(hideBin(argv))
|
|
16
13
|
.wrap(Math.min(140, process.stdout.columns))
|
|
17
14
|
.scriptName('hcm')
|
|
18
|
-
.usage('
|
|
15
|
+
.usage('Generate .d.ts and .d.ts.map for CSS modules.\n\n$0 [options] <glob>')
|
|
19
16
|
.example("$0 'src/**/*.module.css'", 'Generate .d.ts and .d.ts.map.')
|
|
20
17
|
.example("$0 'src/**/*.module.{css,scss,less}'", 'Also generate files for sass and less.')
|
|
21
18
|
.example("$0 'src/**/*.module.css' --watch", 'Watch for changes and generate .d.ts and .d.ts.map.')
|
|
@@ -23,11 +20,8 @@ export function parseArgv(argv: string[]): RunnerOptions {
|
|
|
23
20
|
.example("$0 'src/**/*.module.css' --sassLoadPaths=src/style", "Run with sass's `--load-path`.")
|
|
24
21
|
.example("$0 'src/**/*.module.css' --lessIncludePaths=src/style", "Run with less's `--include-path`.")
|
|
25
22
|
.example('$0 \'src/**/*.module.css\' --webpackResolveAlias=\'{"@": "src"}\'', "Run with webpack's `resolve.alias`.")
|
|
23
|
+
.example("$0 'src/**/*.module.css' --cache=false", 'Disable cache.')
|
|
26
24
|
.detectLocale(false)
|
|
27
|
-
.option('outDir', {
|
|
28
|
-
type: 'string',
|
|
29
|
-
describe: 'Output directory',
|
|
30
|
-
})
|
|
31
25
|
.option('watch', {
|
|
32
26
|
type: 'boolean',
|
|
33
27
|
alias: 'w',
|
|
@@ -62,6 +56,17 @@ export function parseArgv(argv: string[]): RunnerOptions {
|
|
|
62
56
|
string: true,
|
|
63
57
|
describe: "The option compatible with postcss's `--config`.",
|
|
64
58
|
})
|
|
59
|
+
.option('cache', {
|
|
60
|
+
type: 'boolean',
|
|
61
|
+
default: true,
|
|
62
|
+
describe: 'Only generate .d.ts and .d.ts.map for changed files.',
|
|
63
|
+
})
|
|
64
|
+
.option('cacheStrategy', {
|
|
65
|
+
choices: ['content', 'metadata'] as const,
|
|
66
|
+
// NOTE: This is a workaround for `parsedArgv.cacheStrategy` type breaks.
|
|
67
|
+
default: 'content' as RunnerOptions['cacheStrategy'],
|
|
68
|
+
describe: 'Strategy for the cache to use for detecting changed files.',
|
|
69
|
+
})
|
|
65
70
|
.option('silent', {
|
|
66
71
|
type: 'boolean',
|
|
67
72
|
default: false,
|
|
@@ -94,7 +99,6 @@ export function parseArgv(argv: string[]): RunnerOptions {
|
|
|
94
99
|
const patterns: string[] = parsedArgv._.map((pattern) => pattern.toString());
|
|
95
100
|
return {
|
|
96
101
|
pattern: patterns[0]!,
|
|
97
|
-
outDir: parsedArgv.outDir,
|
|
98
102
|
watch: parsedArgv.watch,
|
|
99
103
|
localsConvention: parsedArgv.localsConvention,
|
|
100
104
|
declarationMap: parsedArgv.declarationMap,
|
|
@@ -102,6 +106,8 @@ export function parseArgv(argv: string[]): RunnerOptions {
|
|
|
102
106
|
lessIncludePaths: parsedArgv.lessIncludePaths?.map((item) => item.toString()),
|
|
103
107
|
webpackResolveAlias: parsedArgv.webpackResolveAlias ? JSON.parse(parsedArgv.webpackResolveAlias) : undefined,
|
|
104
108
|
postcssConfig: parsedArgv.postcssConfig,
|
|
109
|
+
cache: parsedArgv.cache,
|
|
110
|
+
cacheStrategy: parsedArgv.cacheStrategy,
|
|
105
111
|
silent: parsedArgv.silent,
|
|
106
112
|
};
|
|
107
113
|
}
|
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 {
|
|
@@ -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
|
@@ -7,3 +7,4 @@ export {
|
|
|
7
7
|
createDefaultTransformer,
|
|
8
8
|
} from './transformer/index.js';
|
|
9
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';
|
|
@@ -15,10 +15,10 @@ jest.unstable_mockModule('fs/promises', () => ({
|
|
|
15
15
|
|
|
16
16
|
// After the mock of fs/promises is complete, . /index.js after the mock of fs/promises is complete.
|
|
17
17
|
// ref: https://www.coolcomputerclub.com/posts/jest-hoist-await/
|
|
18
|
-
const {
|
|
18
|
+
const { Locator } = await import('./index.js');
|
|
19
19
|
// NOTE: ../test/util.js depends on . /index.js, so it must also be imported dynamically...
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const locator = new Locator();
|
|
22
22
|
|
|
23
23
|
afterEach(() => {
|
|
24
24
|
readFileSpy.mockClear();
|
|
@@ -31,7 +31,7 @@ test('basic', async () => {
|
|
|
31
31
|
.b {}
|
|
32
32
|
`,
|
|
33
33
|
});
|
|
34
|
-
const result = await
|
|
34
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
35
35
|
expect(result).toMatchInlineSnapshot(`
|
|
36
36
|
{
|
|
37
37
|
dependencies: [],
|
|
@@ -77,7 +77,7 @@ test('tracks other files when `@import` is present', async () => {
|
|
|
77
77
|
.d {}
|
|
78
78
|
`,
|
|
79
79
|
});
|
|
80
|
-
const result = await
|
|
80
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
81
81
|
expect(result).toMatchInlineSnapshot(`
|
|
82
82
|
{
|
|
83
83
|
dependencies: [
|
|
@@ -137,7 +137,7 @@ test('tracks other files when `composes` is present', async () => {
|
|
|
137
137
|
.e {}
|
|
138
138
|
`,
|
|
139
139
|
});
|
|
140
|
-
const result = await
|
|
140
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
141
141
|
expect(result).toMatchInlineSnapshot(`
|
|
142
142
|
{
|
|
143
143
|
dependencies: ["<fixtures>/test/2.css", "<fixtures>/test/3.css", "<fixtures>/test/4.css"],
|
|
@@ -201,7 +201,7 @@ test('normalizes tokens', async () => {
|
|
|
201
201
|
.c {}
|
|
202
202
|
`,
|
|
203
203
|
});
|
|
204
|
-
const result = await
|
|
204
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
205
205
|
expect(result).toMatchInlineSnapshot(`
|
|
206
206
|
{
|
|
207
207
|
dependencies: ["<fixtures>/test/2.css", "<fixtures>/test/3.css"],
|
|
@@ -250,7 +250,7 @@ test.failing('returns the result from the cache when the file has not been modif
|
|
|
250
250
|
.d {}
|
|
251
251
|
`,
|
|
252
252
|
});
|
|
253
|
-
await
|
|
253
|
+
await locator.load(getFixturePath('/test/1.css'));
|
|
254
254
|
expect(readFileSpy).toHaveBeenCalledTimes(3);
|
|
255
255
|
expect(readFileSpy).toHaveBeenNthCalledWith(1, '/test/1.css', 'utf-8');
|
|
256
256
|
expect(readFileSpy).toHaveBeenNthCalledWith(2, '/test/2.css', 'utf-8');
|
|
@@ -262,17 +262,17 @@ test.failing('returns the result from the cache when the file has not been modif
|
|
|
262
262
|
await writeFile(getFixturePath('/test/2.css'), await readFile(getFixturePath('/test/2.css'), 'utf-8'));
|
|
263
263
|
|
|
264
264
|
// `3.css` is not updated, so the cache is used. Therefore, `readFile` is not called.
|
|
265
|
-
await
|
|
265
|
+
await locator.load(getFixturePath('/test/3.css'));
|
|
266
266
|
expect(readFileSpy).toHaveBeenCalledTimes(0);
|
|
267
267
|
|
|
268
268
|
// `1.css` is not updated, but dependencies are updated, so the cache is used. Therefore, `readFile` is called.
|
|
269
|
-
await
|
|
269
|
+
await locator.load(getFixturePath('/test/1.css'));
|
|
270
270
|
expect(readFileSpy).toHaveBeenCalledTimes(2);
|
|
271
271
|
expect(readFileSpy).toHaveBeenNthCalledWith(1, '/test/1.css', 'utf-8');
|
|
272
272
|
expect(readFileSpy).toHaveBeenNthCalledWith(2, '/test/2.css', 'utf-8');
|
|
273
273
|
|
|
274
274
|
// ``2.css` is updated, but the cache is already available because it was updated in the previous step. Therefore, `readFile` is not called.
|
|
275
|
-
await
|
|
275
|
+
await locator.load(getFixturePath('/test/2.css'));
|
|
276
276
|
expect(readFileSpy).toHaveBeenCalledTimes(2);
|
|
277
277
|
});
|
|
278
278
|
|
|
@@ -291,7 +291,7 @@ test('ignores the composition of non-existent tokens', async () => {
|
|
|
291
291
|
.b {}
|
|
292
292
|
`,
|
|
293
293
|
});
|
|
294
|
-
const result = await
|
|
294
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
295
295
|
expect(result.tokens.map((t) => t.name)).toStrictEqual(['a', 'b']);
|
|
296
296
|
});
|
|
297
297
|
|
|
@@ -306,7 +306,7 @@ test('throws error the composition of non-existent file', async () => {
|
|
|
306
306
|
`,
|
|
307
307
|
});
|
|
308
308
|
await expect(async () => {
|
|
309
|
-
await
|
|
309
|
+
await locator.load(getFixturePath('/test/1.css')).catch((e) => {
|
|
310
310
|
e.message = e.message.replace(FIXTURE_DIR_PATH, '<fixtures>');
|
|
311
311
|
throw e;
|
|
312
312
|
});
|
|
@@ -316,7 +316,7 @@ test('throws error the composition of non-existent file', async () => {
|
|
|
316
316
|
describe('supports sourcemap', () => {
|
|
317
317
|
test('restores original locations from sourcemap', async () => {
|
|
318
318
|
const transformer = createDefaultTransformer();
|
|
319
|
-
const
|
|
319
|
+
const locator = new Locator({ transformer });
|
|
320
320
|
createFixtures({
|
|
321
321
|
'/test/1.scss': dedent`
|
|
322
322
|
.nesting {
|
|
@@ -327,7 +327,7 @@ describe('supports sourcemap', () => {
|
|
|
327
327
|
}
|
|
328
328
|
`,
|
|
329
329
|
});
|
|
330
|
-
const result = await
|
|
330
|
+
const result = await locator.load(getFixturePath('/test/1.scss'));
|
|
331
331
|
expect(result).toMatchInlineSnapshot(`
|
|
332
332
|
{
|
|
333
333
|
dependencies: [],
|
|
@@ -365,8 +365,8 @@ describe('supports sourcemap', () => {
|
|
|
365
365
|
`,
|
|
366
366
|
});
|
|
367
367
|
const transformer = createDefaultTransformer({ postcssConfig: getFixturePath(`/${uuid}/postcss.config.js`) });
|
|
368
|
-
const
|
|
369
|
-
const result = await
|
|
368
|
+
const locator = new Locator({ transformer });
|
|
369
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
370
370
|
expect(result).toMatchInlineSnapshot(`
|
|
371
371
|
{
|
|
372
372
|
dependencies: [],
|
|
@@ -398,6 +398,15 @@ test('ignores http(s) protocol file', async () => {
|
|
|
398
398
|
@import 'https://example.com/path/https.css';
|
|
399
399
|
`,
|
|
400
400
|
});
|
|
401
|
-
const result = await
|
|
401
|
+
const result = await locator.load(getFixturePath('/test/1.css'));
|
|
402
402
|
expect(result.dependencies).toStrictEqual([]);
|
|
403
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
|
|
File without changes
|