happy-css-modules 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/LICENSE.txt +23 -0
  2. package/README.md +124 -0
  3. package/bin/hcm.js +9 -0
  4. package/dist/cli.d.ts +6 -0
  5. package/dist/cli.js +69 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/cli.test.d.ts +1 -0
  8. package/dist/cli.test.js +34 -0
  9. package/dist/cli.test.js.map +1 -0
  10. package/dist/emitter/dts.d.ts +14 -0
  11. package/dist/emitter/dts.js +106 -0
  12. package/dist/emitter/dts.js.map +1 -0
  13. package/dist/emitter/dts.test.d.ts +1 -0
  14. package/dist/emitter/dts.test.js +205 -0
  15. package/dist/emitter/dts.test.js.map +1 -0
  16. package/dist/emitter/file-system.d.ts +6 -0
  17. package/dist/emitter/file-system.js +26 -0
  18. package/dist/emitter/file-system.js.map +1 -0
  19. package/dist/emitter/file-system.test.d.ts +1 -0
  20. package/dist/emitter/file-system.test.js +34 -0
  21. package/dist/emitter/file-system.test.js.map +1 -0
  22. package/dist/emitter/index.d.ts +34 -0
  23. package/dist/emitter/index.js +42 -0
  24. package/dist/emitter/index.js.map +1 -0
  25. package/dist/emitter/index.test.d.ts +1 -0
  26. package/dist/emitter/index.test.js +118 -0
  27. package/dist/emitter/index.test.js.map +1 -0
  28. package/dist/emitter/source-map.d.ts +9 -0
  29. package/dist/emitter/source-map.js +16 -0
  30. package/dist/emitter/source-map.js.map +1 -0
  31. package/dist/emitter/source-map.test.d.ts +1 -0
  32. package/dist/emitter/source-map.test.js +13 -0
  33. package/dist/emitter/source-map.test.js.map +1 -0
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.js +3 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/integration-test/go-to-definition.test.d.ts +1 -0
  38. package/dist/integration-test/go-to-definition.test.js +367 -0
  39. package/dist/integration-test/go-to-definition.test.js.map +1 -0
  40. package/dist/library/source-map/index.d.ts +8 -0
  41. package/dist/library/source-map/index.js +5 -0
  42. package/dist/library/source-map/index.js.map +1 -0
  43. package/dist/loader/index.d.ts +42 -0
  44. package/dist/loader/index.js +145 -0
  45. package/dist/loader/index.js.map +1 -0
  46. package/dist/loader/index.test.d.ts +1 -0
  47. package/dist/loader/index.test.js +290 -0
  48. package/dist/loader/index.test.js.map +1 -0
  49. package/dist/loader/postcss.d.ts +60 -0
  50. package/dist/loader/postcss.js +209 -0
  51. package/dist/loader/postcss.js.map +1 -0
  52. package/dist/loader/postcss.test.d.ts +1 -0
  53. package/dist/loader/postcss.test.js +236 -0
  54. package/dist/loader/postcss.test.js.map +1 -0
  55. package/dist/resolver/index.d.ts +20 -0
  56. package/dist/resolver/index.js +39 -0
  57. package/dist/resolver/index.js.map +1 -0
  58. package/dist/resolver/index.test.d.ts +1 -0
  59. package/dist/resolver/index.test.js +16 -0
  60. package/dist/resolver/index.test.js.map +1 -0
  61. package/dist/resolver/node-resolver.d.ts +2 -0
  62. package/dist/resolver/node-resolver.js +6 -0
  63. package/dist/resolver/node-resolver.js.map +1 -0
  64. package/dist/resolver/node-resolver.test.d.ts +1 -0
  65. package/dist/resolver/node-resolver.test.js +25 -0
  66. package/dist/resolver/node-resolver.test.js.map +1 -0
  67. package/dist/resolver/relative-resolver.d.ts +2 -0
  68. package/dist/resolver/relative-resolver.js +5 -0
  69. package/dist/resolver/relative-resolver.js.map +1 -0
  70. package/dist/resolver/relative-resolver.test.d.ts +1 -0
  71. package/dist/resolver/relative-resolver.test.js +12 -0
  72. package/dist/resolver/relative-resolver.test.js.map +1 -0
  73. package/dist/resolver/webpack-resolver.d.ts +2 -0
  74. package/dist/resolver/webpack-resolver.js +69 -0
  75. package/dist/resolver/webpack-resolver.js.map +1 -0
  76. package/dist/resolver/webpack-resolver.test.d.ts +1 -0
  77. package/dist/resolver/webpack-resolver.test.js +26 -0
  78. package/dist/resolver/webpack-resolver.test.js.map +1 -0
  79. package/dist/runner.d.ts +33 -0
  80. package/dist/runner.js +75 -0
  81. package/dist/runner.js.map +1 -0
  82. package/dist/runner.test.d.ts +1 -0
  83. package/dist/runner.test.js +113 -0
  84. package/dist/runner.test.js.map +1 -0
  85. package/dist/test/jest/resolver.cjs +30 -0
  86. package/dist/test/jest/resolver.cjs.map +1 -0
  87. package/dist/test/jest/resolver.d.cts +30 -0
  88. package/dist/test/tsserver.d.ts +27 -0
  89. package/dist/test/tsserver.js +104 -0
  90. package/dist/test/tsserver.js.map +1 -0
  91. package/dist/test/util.d.ts +29 -0
  92. package/dist/test/util.js +78 -0
  93. package/dist/test/util.js.map +1 -0
  94. package/dist/transformer/index.d.ts +23 -0
  95. package/dist/transformer/index.js +19 -0
  96. package/dist/transformer/index.js.map +1 -0
  97. package/dist/transformer/less-transformer.d.ts +2 -0
  98. package/dist/transformer/less-transformer.js +43 -0
  99. package/dist/transformer/less-transformer.js.map +1 -0
  100. package/dist/transformer/less-transformer.test.d.ts +1 -0
  101. package/dist/transformer/less-transformer.test.js +126 -0
  102. package/dist/transformer/less-transformer.test.js.map +1 -0
  103. package/dist/transformer/scss-transformer.d.ts +2 -0
  104. package/dist/transformer/scss-transformer.js +84 -0
  105. package/dist/transformer/scss-transformer.js.map +1 -0
  106. package/dist/transformer/scss-transformer.test.d.ts +1 -0
  107. package/dist/transformer/scss-transformer.test.js +132 -0
  108. package/dist/transformer/scss-transformer.test.js.map +1 -0
  109. package/dist/util.d.ts +19 -0
  110. package/dist/util.js +52 -0
  111. package/dist/util.js.map +1 -0
  112. package/dist/util.test.d.ts +1 -0
  113. package/dist/util.test.js +75 -0
  114. package/dist/util.test.js.map +1 -0
  115. package/package.json +106 -0
  116. package/src/__snapshots__/runner.test.ts.snap +34 -0
  117. package/src/cli.test.ts +38 -0
  118. package/src/cli.ts +70 -0
  119. package/src/emitter/dts.test.ts +266 -0
  120. package/src/emitter/dts.ts +134 -0
  121. package/src/emitter/file-system.test.ts +36 -0
  122. package/src/emitter/file-system.ts +24 -0
  123. package/src/emitter/index.test.ts +130 -0
  124. package/src/emitter/index.ts +92 -0
  125. package/src/emitter/source-map.test.ts +20 -0
  126. package/src/emitter/source-map.ts +17 -0
  127. package/src/index.ts +3 -0
  128. package/src/integration-test/go-to-definition.test.ts +371 -0
  129. package/src/library/README.md +3 -0
  130. package/src/library/source-map/index.ts +26 -0
  131. package/src/loader/index.test.ts +306 -0
  132. package/src/loader/index.ts +199 -0
  133. package/src/loader/postcss.test.ts +336 -0
  134. package/src/loader/postcss.ts +239 -0
  135. package/src/resolver/index.test.ts +17 -0
  136. package/src/resolver/index.ts +48 -0
  137. package/src/resolver/node-resolver.test.ts +26 -0
  138. package/src/resolver/node-resolver.ts +7 -0
  139. package/src/resolver/relative-resolver.test.ts +13 -0
  140. package/src/resolver/relative-resolver.ts +6 -0
  141. package/src/resolver/webpack-resolver.test.ts +33 -0
  142. package/src/resolver/webpack-resolver.ts +71 -0
  143. package/src/runner.test.ts +122 -0
  144. package/src/runner.ts +105 -0
  145. package/src/test/jest/resolver.cjs +30 -0
  146. package/src/test/tsserver.ts +176 -0
  147. package/src/test/util.ts +100 -0
  148. package/src/transformer/index.ts +44 -0
  149. package/src/transformer/less-transformer.test.ts +135 -0
  150. package/src/transformer/less-transformer.ts +55 -0
  151. package/src/transformer/scss-transformer.test.ts +142 -0
  152. package/src/transformer/scss-transformer.ts +94 -0
  153. package/src/util.test.ts +89 -0
  154. package/src/util.ts +67 -0
@@ -0,0 +1,130 @@
1
+ import { readFile, stat } from 'fs/promises';
2
+ import { jest } from '@jest/globals';
3
+ import chalk from 'chalk';
4
+ import { createFixtures, exists, fakeToken, getFixturePath, waitForAsyncTask } from '../test/util.js';
5
+ import { emitGeneratedFiles, getRelativePath, isSubDirectoryFile } from './index.js';
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
8
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
9
+
10
+ beforeEach(() => {
11
+ consoleLogSpy.mockClear();
12
+ });
13
+
14
+ test('getRelativePath', () => {
15
+ expect(getRelativePath('/test/1.css.d.ts', '/test/1.css')).toBe('./1.css');
16
+ expect(getRelativePath('/test/1.css.d.ts', '/test/dir/1.css')).toBe('./dir/1.css');
17
+ expect(getRelativePath('/test/1.css.d.ts', '/1.css')).toBe('../1.css');
18
+ });
19
+
20
+ test('isSubDirectoryFile', () => {
21
+ expect(isSubDirectoryFile('/test', '/test/src/1.css')).toBe(true);
22
+ expect(isSubDirectoryFile('/test', '/test/dist/1.css')).toBe(true);
23
+ expect(isSubDirectoryFile('/test', '/1.css')).toBe(false);
24
+ });
25
+
26
+ describe('emitGeneratedFiles', () => {
27
+ const defaultArgs = {
28
+ filePath: getFixturePath('/test/1.css'),
29
+ tokens: [fakeToken({ name: 'foo', originalLocations: [{ start: { line: 1, column: 1 } }] })],
30
+ distOptions: undefined,
31
+ emitDeclarationMap: true,
32
+ dtsFormatOptions: undefined,
33
+ silent: true,
34
+ cwd: getFixturePath('/test'),
35
+ isExternalFile: () => false,
36
+ };
37
+ beforeEach(() => {
38
+ createFixtures({
39
+ '/test': {}, // empty directory
40
+ });
41
+ });
42
+ test('generates .d.ts and .d.ts.map', async () => {
43
+ await emitGeneratedFiles({ ...defaultArgs });
44
+ expect(await exists(getFixturePath('/test/1.css.d.ts'))).toBeTruthy();
45
+ // A link to the source map is embedded.
46
+ expect(await readFile(getFixturePath('/test/1.css.d.ts'), 'utf8')).toEqual(
47
+ expect.stringContaining('//# sourceMappingURL=./1.css.d.ts.map'),
48
+ );
49
+ expect(await exists(getFixturePath('/test/1.css.d.ts.map'))).toBeTruthy();
50
+ });
51
+ test('generates only .d.ts and .d.ts.map if emitDeclarationMap is false', async () => {
52
+ await emitGeneratedFiles({ ...defaultArgs, emitDeclarationMap: false });
53
+ expect(await exists(getFixturePath('/test/1.css.d.ts'))).toBeTruthy();
54
+ // A link to the source map is not embedded.
55
+ expect(await readFile(getFixturePath('/test/1.css.d.ts'), 'utf8')).toEqual(
56
+ expect.not.stringContaining('//# sourceMappingURL=1.css.d.ts.map'),
57
+ );
58
+ expect(await exists(getFixturePath('/test/1.css.d.ts.map'))).toBeFalsy();
59
+ });
60
+ test('skips writing to disk if the generated files are the same', async () => {
61
+ const tokens1 = [fakeToken({ name: 'foo', originalLocations: [{ start: { line: 1, column: 1 } }] })];
62
+ await emitGeneratedFiles({ ...defaultArgs, tokens: tokens1 });
63
+ const mtimeForDts1 = (await stat(getFixturePath('/test/1.css.d.ts'))).mtime;
64
+ const mtimeForSourceMap1 = (await stat(getFixturePath('/test/1.css.d.ts.map'))).mtime;
65
+
66
+ await waitForAsyncTask(1); // so that mtime changes.
67
+ await emitGeneratedFiles({ ...defaultArgs, tokens: tokens1 });
68
+ const mtimeForDts2 = (await stat(getFixturePath('/test/1.css.d.ts'))).mtime;
69
+ const mtimeForSourceMap2 = (await stat(getFixturePath('/test/1.css.d.ts.map'))).mtime;
70
+ expect(mtimeForDts1).toEqual(mtimeForDts2); // skipped
71
+ expect(mtimeForSourceMap1).toEqual(mtimeForSourceMap2); // skipped
72
+
73
+ await waitForAsyncTask(1); // so that mtime changes.
74
+ const tokens2 = [fakeToken({ name: 'bar', originalLocations: [{ start: { line: 1, column: 1 } }] })];
75
+ await emitGeneratedFiles({ ...defaultArgs, tokens: tokens2 });
76
+ const mtimeForDts3 = (await stat(getFixturePath('/test/1.css.d.ts'))).mtime;
77
+ const mtimeForSourceMap3 = (await stat(getFixturePath('/test/1.css.d.ts.map'))).mtime;
78
+ expect(mtimeForDts1).not.toEqual(mtimeForDts3); // not skipped
79
+ expect(mtimeForSourceMap1).not.toEqual(mtimeForSourceMap3); // not skipped
80
+ });
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
+ });
@@ -0,0 +1,92 @@
1
+ import { dirname, isAbsolute, relative } from 'path';
2
+ import chalk from 'chalk';
3
+ import { type Token } from '../loader/index.js';
4
+ import { type LocalsConvention } from '../runner.js';
5
+ import { generateDtsContentWithSourceMap, getDtsFilePath } from './dts.js';
6
+ import { writeFileIfChanged } from './file-system.js';
7
+ import { generateSourceMappingURLComment, getSourceMapFilePath } from './source-map.js';
8
+
9
+ export function getRelativePath(fromFilePath: string, toFilePath: string): string {
10
+ const resolved = relative(dirname(fromFilePath), toFilePath);
11
+ if (resolved.startsWith('..')) {
12
+ return resolved;
13
+ } else {
14
+ return './' + resolved;
15
+ }
16
+ }
17
+
18
+ export function isSubDirectoryFile(fromDirectory: string, toFilePath: string): boolean {
19
+ return isAbsolute(toFilePath) && toFilePath.startsWith(fromDirectory);
20
+ }
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
+ export type DtsFormatOptions = {
35
+ localsConvention?: LocalsConvention;
36
+ };
37
+
38
+ /** The options for emitter. */
39
+ export type EmitterOptions = {
40
+ /** The path to the source file (i.e. `/dir/foo.css`). It is absolute. */
41
+ filePath: string;
42
+ /** The tokens exported by the source file. */
43
+ tokens: Token[];
44
+ /** The distribution option. */
45
+ distOptions: DistOptions | undefined;
46
+ /** Whether to output declaration map (i.e. `/dir/foo.css.d.ts.map`) or not. */
47
+ emitDeclarationMap: boolean | undefined;
48
+ /** The options for formatting the type definition. */
49
+ dtsFormatOptions: DtsFormatOptions | undefined;
50
+ /** Silent output. Do not show "files written" messages */
51
+ silent: boolean;
52
+ /** Working directory path. */
53
+ cwd: string;
54
+ /** Whether the file is from an external library or not. */
55
+ isExternalFile: (filePath: string) => boolean;
56
+ };
57
+
58
+ export async function emitGeneratedFiles({
59
+ filePath,
60
+ tokens,
61
+ distOptions,
62
+ emitDeclarationMap,
63
+ dtsFormatOptions,
64
+ silent,
65
+ cwd,
66
+ isExternalFile,
67
+ }: EmitterOptions): Promise<void> {
68
+ const dtsFilePath = getDtsFilePath(filePath, distOptions);
69
+ const sourceMapFilePath = getSourceMapFilePath(filePath, distOptions);
70
+ const { dtsContent, sourceMap } = generateDtsContentWithSourceMap(
71
+ filePath,
72
+ dtsFilePath,
73
+ sourceMapFilePath,
74
+ tokens,
75
+ dtsFormatOptions,
76
+ isExternalFile,
77
+ );
78
+
79
+ if (emitDeclarationMap) {
80
+ const sourceMappingURLComment = generateSourceMappingURLComment(dtsFilePath, sourceMapFilePath);
81
+ await writeFileIfChanged(dtsFilePath, dtsContent + sourceMappingURLComment);
82
+ if (!silent) outputWriteLog(cwd, dtsFilePath);
83
+
84
+ // NOTE: tsserver does not support inline declaration maps. Therefore, sourcemap files must be output.
85
+ // blocked by: https://github.com/microsoft/TypeScript/issues/38966
86
+ await writeFileIfChanged(sourceMapFilePath, sourceMap.toString());
87
+ if (!silent) outputWriteLog(cwd, sourceMapFilePath);
88
+ } else {
89
+ await writeFileIfChanged(dtsFilePath, dtsContent);
90
+ if (!silent) outputWriteLog(cwd, dtsFilePath);
91
+ }
92
+ }
@@ -0,0 +1,20 @@
1
+ import { EOL } from 'os';
2
+ import { getSourceMapFilePath, generateSourceMappingURLComment } from './source-map.js';
3
+
4
+ test('getSourceMapFilePath', () => {
5
+ expect(getSourceMapFilePath('/app/src/dir/1.css', undefined)).toBe('/app/src/dir/1.css.d.ts.map');
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();
11
+ });
12
+
13
+ test('generateSourceMappingURLComment', () => {
14
+ expect(generateSourceMappingURLComment('/app/src/dir/1.css.d.ts', '/app/src/dir/1.css.d.ts.map')).toBe(
15
+ '//# sourceMappingURL=./1.css.d.ts.map' + EOL,
16
+ );
17
+ expect(generateSourceMappingURLComment('/app/src/dir1/1.css.d.ts', '/app/src/dir2/1.css.d.ts.map')).toBe(
18
+ '//# sourceMappingURL=../dir2/1.css.d.ts.map' + EOL,
19
+ );
20
+ });
@@ -0,0 +1,17 @@
1
+ import { EOL } from 'os';
2
+ import { getDtsFilePath } from './dts.js';
3
+ import { type DistOptions, getRelativePath } from './index.js';
4
+
5
+ /**
6
+ * Get .d.ts.map file path.
7
+ * @param filePath The path to the source file (i.e. `foo.css`). It is absolute.
8
+ * @param distOptions The distribution option.
9
+ * @returns The path to the .d.ts.map file. It is absolute.
10
+ */
11
+ export function getSourceMapFilePath(filePath: string, distOptions: DistOptions | undefined): string {
12
+ return getDtsFilePath(filePath, distOptions) + '.map';
13
+ }
14
+
15
+ export function generateSourceMappingURLComment(dtsFilePath: string, sourceMapFilePath: string): string {
16
+ return `//# sourceMappingURL=${getRelativePath(dtsFilePath, sourceMapFilePath)}` + EOL;
17
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { parseArgv } from './cli.js';
2
+ export { run } from './runner.js';
3
+ export { type Transformer } from './transformer/index.js';
@@ -0,0 +1,371 @@
1
+ import { jest } from '@jest/globals';
2
+ import dedent from 'dedent';
3
+ import { run } from '../runner.js';
4
+ import { getModuleDefinitions, getMultipleIdentifierDefinitions } from '../test/tsserver.js';
5
+ import { createFixtures, getFixturePath } from '../test/util.js';
6
+
7
+ // It is heavy test, so increase timeout.
8
+ jest.setTimeout(60 * 1000); // 60s
9
+
10
+ const defaultOptions = {
11
+ pattern: 'test/**/*.{css,scss}',
12
+ silent: true,
13
+ declarationMap: true,
14
+ cwd: getFixturePath('/'),
15
+ };
16
+
17
+ test('basic', async () => {
18
+ createFixtures({
19
+ '/test/1.css': dedent`
20
+ .basic {}
21
+ .cascading {}
22
+ .cascading {}
23
+ .pseudo_class_1 {}
24
+ .pseudo_class_2:hover {}
25
+ :not(.pseudo_class_3) {}
26
+ .multiple_selector_1.multiple_selector_2 {}
27
+ .combinator_1 + .combinator_2 {}
28
+ @supports (display: flex) {
29
+ @media screen and (min-width: 900px) {
30
+ .at_rule {}
31
+ }
32
+ }
33
+ .selector_list_1, .selector_list_2 {}
34
+ :local .local_class_name_1 {}
35
+ :local {
36
+ .local_class_name_2 {}
37
+ .local_class_name_3 {}
38
+ }
39
+ :local(.local_class_name_4) {}
40
+ .composes_target {}
41
+ .composes {
42
+ composes: composes_target;
43
+ }
44
+ `,
45
+ });
46
+ await run({ ...defaultOptions });
47
+ const results = await getMultipleIdentifierDefinitions(getFixturePath(`/test/1.css`), [
48
+ 'basic',
49
+ 'cascading',
50
+ 'pseudo_class_1',
51
+ 'pseudo_class_2',
52
+ 'pseudo_class_3',
53
+ 'multiple_selector_1',
54
+ 'multiple_selector_2',
55
+ 'combinator_1',
56
+ 'combinator_2',
57
+ 'at_rule',
58
+ 'selector_list_1',
59
+ 'selector_list_2',
60
+ 'local_class_name_1',
61
+ 'local_class_name_2',
62
+ 'local_class_name_3',
63
+ 'local_class_name_4',
64
+ 'composes_target',
65
+ 'composes',
66
+ ]);
67
+ // FIXME: Fix an issue where the text at definition destination was incorrect.
68
+ expect(results).toMatchInlineSnapshot(`
69
+ [
70
+ {
71
+ "definitions": [
72
+ { file: "<fixtures>/test/1.css", text: ".basic ", start: { line: 1, offset: 1 }, end: { line: 1, offset: 8 } },
73
+ ],
74
+ "identifier": "basic",
75
+ },
76
+ {
77
+ "definitions": [
78
+ { file: "<fixtures>/test/1.css", text: ".cascading ", start: { line: 2, offset: 1 }, end: { line: 2, offset: 12 } },
79
+ { file: "<fixtures>/test/1.css", text: ".cascading ", start: { line: 3, offset: 1 }, end: { line: 3, offset: 12 } },
80
+ ],
81
+ "identifier": "cascading",
82
+ },
83
+ {
84
+ "definitions": [
85
+ { file: "<fixtures>/test/1.css", text: ".pseudo_class_1 ", start: { line: 4, offset: 1 }, end: { line: 4, offset: 17 } },
86
+ ],
87
+ "identifier": "pseudo_class_1",
88
+ },
89
+ {
90
+ "definitions": [
91
+ { file: "<fixtures>/test/1.css", text: ".pseudo_class_2:", start: { line: 5, offset: 1 }, end: { line: 5, offset: 17 } },
92
+ ],
93
+ "identifier": "pseudo_class_2",
94
+ },
95
+ {
96
+ "definitions": [
97
+ { file: "<fixtures>/test/1.css", text: ".pseudo_class_3)", start: { line: 6, offset: 6 }, end: { line: 6, offset: 22 } },
98
+ ],
99
+ "identifier": "pseudo_class_3",
100
+ },
101
+ {
102
+ "definitions": [
103
+ {
104
+ file: "<fixtures>/test/1.css",
105
+ text: ".multiple_selector_1.",
106
+ start: { line: 7, offset: 1 },
107
+ end: { line: 7, offset: 22 },
108
+ },
109
+ ],
110
+ "identifier": "multiple_selector_1",
111
+ },
112
+ {
113
+ "definitions": [
114
+ {
115
+ file: "<fixtures>/test/1.css",
116
+ text: ".multiple_selector_2 ",
117
+ start: { line: 7, offset: 21 },
118
+ end: { line: 7, offset: 42 },
119
+ },
120
+ ],
121
+ "identifier": "multiple_selector_2",
122
+ },
123
+ {
124
+ "definitions": [
125
+ { file: "<fixtures>/test/1.css", text: ".combinator_1 ", start: { line: 8, offset: 1 }, end: { line: 8, offset: 15 } },
126
+ ],
127
+ "identifier": "combinator_1",
128
+ },
129
+ {
130
+ "definitions": [
131
+ { file: "<fixtures>/test/1.css", text: ".combinator_2 ", start: { line: 8, offset: 17 }, end: { line: 8, offset: 31 } },
132
+ ],
133
+ "identifier": "combinator_2",
134
+ },
135
+ {
136
+ "definitions": [
137
+ { file: "<fixtures>/test/1.css", text: ".at_rule ", start: { line: 11, offset: 5 }, end: { line: 11, offset: 14 } },
138
+ ],
139
+ "identifier": "at_rule",
140
+ },
141
+ {
142
+ "definitions": [
143
+ {
144
+ file: "<fixtures>/test/1.css",
145
+ text: ".selector_list_1,",
146
+ start: { line: 14, offset: 1 },
147
+ end: { line: 14, offset: 18 },
148
+ },
149
+ ],
150
+ "identifier": "selector_list_1",
151
+ },
152
+ {
153
+ "definitions": [
154
+ {
155
+ file: "<fixtures>/test/1.css",
156
+ text: ".selector_list_2 ",
157
+ start: { line: 14, offset: 19 },
158
+ end: { line: 14, offset: 36 },
159
+ },
160
+ ],
161
+ "identifier": "selector_list_2",
162
+ },
163
+ {
164
+ "definitions": [
165
+ {
166
+ file: "<fixtures>/test/1.css",
167
+ text: ".local_class_name_1 ",
168
+ start: { line: 15, offset: 8 },
169
+ end: { line: 15, offset: 28 },
170
+ },
171
+ ],
172
+ "identifier": "local_class_name_1",
173
+ },
174
+ {
175
+ "definitions": [
176
+ {
177
+ file: "<fixtures>/test/1.css",
178
+ text: ".local_class_name_2 ",
179
+ start: { line: 17, offset: 3 },
180
+ end: { line: 17, offset: 23 },
181
+ },
182
+ ],
183
+ "identifier": "local_class_name_2",
184
+ },
185
+ {
186
+ "definitions": [
187
+ {
188
+ file: "<fixtures>/test/1.css",
189
+ text: ".local_class_name_3 ",
190
+ start: { line: 18, offset: 3 },
191
+ end: { line: 18, offset: 23 },
192
+ },
193
+ ],
194
+ "identifier": "local_class_name_3",
195
+ },
196
+ {
197
+ "definitions": [
198
+ {
199
+ file: "<fixtures>/test/1.css",
200
+ text: ".local_class_name_4)",
201
+ start: { line: 20, offset: 8 },
202
+ end: { line: 20, offset: 28 },
203
+ },
204
+ ],
205
+ "identifier": "local_class_name_4",
206
+ },
207
+ {
208
+ "definitions": [
209
+ {
210
+ file: "<fixtures>/test/1.css",
211
+ text: ".composes_target ",
212
+ start: { line: 21, offset: 1 },
213
+ end: { line: 21, offset: 18 },
214
+ },
215
+ ],
216
+ "identifier": "composes_target",
217
+ },
218
+ {
219
+ "definitions": [
220
+ { file: "<fixtures>/test/1.css", text: ".composes ", start: { line: 22, offset: 1 }, end: { line: 22, offset: 11 } },
221
+ ],
222
+ "identifier": "composes",
223
+ },
224
+ ]
225
+ `);
226
+ const moduleDefinitions = await getModuleDefinitions(getFixturePath('/test/1.css'));
227
+ expect(moduleDefinitions).toMatchInlineSnapshot(`
228
+ [
229
+ { file: "<fixtures>/test/1.css", text: "", start: { line: 1, offset: 1 }, end: { line: 1, offset: 1 } },
230
+ ]
231
+ `);
232
+ });
233
+
234
+ test('imported tokens', async () => {
235
+ createFixtures({
236
+ '/test/1.css': dedent`
237
+ @import './2.css';
238
+ .a {}
239
+ .b {}
240
+ .b {}
241
+ `,
242
+ '/test/2.css': dedent`
243
+ @import './3.css';
244
+ .c {}
245
+ `,
246
+ '/test/3.css': dedent`
247
+ .d {}
248
+ `,
249
+ });
250
+ await run({ ...defaultOptions });
251
+ const results = await getMultipleIdentifierDefinitions(getFixturePath(`/test/1.css`), ['a', 'b', 'c', 'd']);
252
+ expect(results).toMatchInlineSnapshot(`
253
+ [
254
+ {
255
+ "definitions": [
256
+ { file: "<fixtures>/test/1.css", text: ".a ", start: { line: 2, offset: 1 }, end: { line: 2, offset: 4 } },
257
+ ],
258
+ "identifier": "a",
259
+ },
260
+ {
261
+ "definitions": [
262
+ { file: "<fixtures>/test/1.css", text: ".b ", start: { line: 3, offset: 1 }, end: { line: 3, offset: 4 } },
263
+ { file: "<fixtures>/test/1.css", text: ".b ", start: { line: 4, offset: 1 }, end: { line: 4, offset: 4 } },
264
+ ],
265
+ "identifier": "b",
266
+ },
267
+ {
268
+ "definitions": [
269
+ { file: "<fixtures>/test/2.css", text: ".c ", start: { line: 2, offset: 1 }, end: { line: 2, offset: 4 } },
270
+ ],
271
+ "identifier": "c",
272
+ },
273
+ {
274
+ "definitions": [
275
+ { file: "<fixtures>/test/3.css", text: ".d ", start: { line: 1, offset: 1 }, end: { line: 1, offset: 4 } },
276
+ ],
277
+ "identifier": "d",
278
+ },
279
+ ]
280
+ `);
281
+ });
282
+
283
+ test('with transformer', async () => {
284
+ createFixtures({
285
+ '/test/1.scss': dedent`
286
+ @use './2.scss' as two; // sass feature test (@use)
287
+ @import './3.scss'; // css feature test (@import)
288
+ .basic { dummy: ''; }
289
+ .nesting {
290
+ dummy: '';
291
+ // sass feature test (nesting)
292
+ .nesting_1 { dummy: ''; }
293
+ &_2 { dummy: ''; }
294
+ composes: basic; // css module feature test (composes)
295
+ composes: d from './4.scss'; // css module feature test (composes from other file)
296
+ }
297
+ `,
298
+ '/test/2.scss': dedent`
299
+ .b_1 { dummy: ''; }
300
+ @mixin b_2 { dummy: ''; }
301
+ `,
302
+ '/test/3.scss': dedent`
303
+ .c { dummy: ''; }
304
+ `,
305
+ '/test/4.scss': dedent`
306
+ .d { dummy: ''; }
307
+ `,
308
+ });
309
+ await run({ ...defaultOptions });
310
+ const results = await getMultipleIdentifierDefinitions(getFixturePath(`/test/1.scss`), [
311
+ 'basic',
312
+ 'nesting',
313
+ 'nesting_1',
314
+ 'nesting_2',
315
+ 'b_1',
316
+ 'b_2',
317
+ 'c',
318
+ 'd',
319
+ ]);
320
+ expect(results).toMatchInlineSnapshot(`
321
+ [
322
+ {
323
+ "definitions": [
324
+ { file: "<fixtures>/test/1.scss", text: ".basic ", start: { line: 3, offset: 1 }, end: { line: 3, offset: 8 } },
325
+ ],
326
+ "identifier": "basic",
327
+ },
328
+ {
329
+ "definitions": [
330
+ { file: "<fixtures>/test/1.scss", text: ".nesting ", start: { line: 4, offset: 1 }, end: { line: 4, offset: 10 } },
331
+ { file: "<fixtures>/test/1.scss", text: ".nesting_", start: { line: 7, offset: 3 }, end: { line: 7, offset: 12 } },
332
+ ],
333
+ "identifier": "nesting",
334
+ },
335
+ {
336
+ "definitions": [
337
+ { file: "<fixtures>/test/1.scss", text: ".nesting_1 ", start: { line: 7, offset: 3 }, end: { line: 7, offset: 14 } },
338
+ ],
339
+ "identifier": "nesting_1",
340
+ },
341
+ {
342
+ "definitions": [
343
+ { file: "<fixtures>/test/1.scss", text: "&_2 { dummy", start: { line: 8, offset: 3 }, end: { line: 8, offset: 14 } },
344
+ ],
345
+ "identifier": "nesting_2",
346
+ },
347
+ {
348
+ "definitions": [
349
+ { file: "<fixtures>/test/2.scss", text: ".b_1 ", start: { line: 1, offset: 1 }, end: { line: 1, offset: 6 } },
350
+ ],
351
+ "identifier": "b_1",
352
+ },
353
+ {
354
+ "definitions": [],
355
+ "identifier": "b_2",
356
+ },
357
+ {
358
+ "definitions": [
359
+ { file: "<fixtures>/test/3.scss", text: ".c ", start: { line: 1, offset: 1 }, end: { line: 1, offset: 4 } },
360
+ ],
361
+ "identifier": "c",
362
+ },
363
+ {
364
+ "definitions": [
365
+ { file: "<fixtures>/test/4.scss", text: ".d ", start: { line: 1, offset: 1 }, end: { line: 1, offset: 4 } },
366
+ ],
367
+ "identifier": "d",
368
+ },
369
+ ]
370
+ `);
371
+ });
@@ -0,0 +1,3 @@
1
+ ## library
2
+
3
+ This is a directory to put library forks or wrappers to avoid bugs.
@@ -0,0 +1,26 @@
1
+ import { SourceNode as OriginalSourceNode, type CodeWithSourceMap } from 'source-map';
2
+
3
+ // TODO: Open PR to mozilla/source-map
4
+
5
+ // The type definitions bundled in the source-map package are incorrect.
6
+ // Therefore, the type definitions are overwritten here.
7
+
8
+ type Chunk = string | StrictlyTypedSourceNode | Chunk[];
9
+
10
+ interface StrictlyTypedSourceNode extends OriginalSourceNode {
11
+ // eslint-disable-next-line @typescript-eslint/no-misused-new
12
+ new (line: number | null, column: number | null, source: string | null): StrictlyTypedSourceNode;
13
+ // eslint-disable-next-line @typescript-eslint/no-misused-new
14
+ new (
15
+ line: number | null,
16
+ column: number | null,
17
+ source: string | null,
18
+ chunk?: Chunk,
19
+ name?: string,
20
+ ): StrictlyTypedSourceNode;
21
+ }
22
+
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ const SourceNode: StrictlyTypedSourceNode = OriginalSourceNode as any;
25
+
26
+ export { SourceNode, type CodeWithSourceMap };