@css-modules-kit/codegen 0.6.0 → 0.8.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.
@@ -1,12 +1,14 @@
1
+ import { inspect } from 'node:util';
1
2
  import type { DiagnosticSourceFile } from '@css-modules-kit/core';
2
- import { convertDiagnostic, convertSystemError, type Diagnostic, type SystemError } from '@css-modules-kit/core';
3
+ import { convertDiagnostic, convertSystemError, type Diagnostic, SystemError } from '@css-modules-kit/core';
3
4
  import ts from 'typescript';
4
- import { formatDiagnostics } from './formatter.js';
5
+ import { formatDiagnostics, formatTime } from './formatter.js';
5
6
 
6
7
  export interface Logger {
7
8
  logDiagnostics(diagnostics: Diagnostic[]): void;
8
- logSystemError(error: SystemError): void;
9
- logMessage(message: string): void;
9
+ logError(error: unknown): void;
10
+ logMessage(message: string, options?: { time?: boolean }): void;
11
+ clearScreen(): void;
10
12
  }
11
13
 
12
14
  export function createLogger(cwd: string, pretty: boolean): Logger {
@@ -29,12 +31,24 @@ export function createLogger(cwd: string, pretty: boolean): Logger {
29
31
  );
30
32
  process.stderr.write(result);
31
33
  },
32
- logSystemError(error: SystemError): void {
33
- const result = formatDiagnostics([convertSystemError(error)], host, pretty);
34
- process.stderr.write(result);
34
+ logError(error: unknown): void {
35
+ // NOTE: SystemErrors are errors expected by the css-modules-kit specification and may occur within normal usage.
36
+ // These errors are formatted clearly and concisely. No stack trace is output.
37
+ //
38
+ // All other errors are unexpected errors. To assist in debugging when these errors occur, a stack trace is output.
39
+ if (error instanceof SystemError) {
40
+ const result = formatDiagnostics([convertSystemError(error)], host, pretty);
41
+ process.stderr.write(result);
42
+ } else {
43
+ process.stderr.write(`${inspect(error, { colors: pretty })}\n`);
44
+ }
45
+ },
46
+ logMessage(message: string, options?: { time?: boolean }): void {
47
+ const header = options?.time ? `${formatTime(new Date(), pretty)} ` : '';
48
+ process.stdout.write(`${header}${message}\n`);
35
49
  },
36
- logMessage(message: string): void {
37
- process.stdout.write(`${message}\n`);
50
+ clearScreen(): void {
51
+ process.stdout.write('\x1B[2J\x1B[3J\x1B[H');
38
52
  },
39
53
  };
40
54
  }
package/src/project.ts ADDED
@@ -0,0 +1,242 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import type { CSSModule, Diagnostic } from '@css-modules-kit/core';
3
+ import {
4
+ checkCSSModule,
5
+ type CMKConfig,
6
+ createExportBuilder,
7
+ createMatchesPattern,
8
+ createResolver,
9
+ generateDts,
10
+ getFileNamesByPattern,
11
+ parseCSSModule,
12
+ readConfigFile,
13
+ relative,
14
+ } from '@css-modules-kit/core';
15
+ import ts from 'typescript';
16
+ import { writeDtsFile } from './dts-writer.js';
17
+ import { ReadCSSModuleFileError } from './error.js';
18
+
19
+ interface ProjectArgs {
20
+ project: string;
21
+ }
22
+
23
+ export interface Project {
24
+ config: CMKConfig;
25
+ /** Whether the file matches the wildcard patterns in `include` / `exclude` options */
26
+ isWildcardMatchedFile(fileName: string): boolean;
27
+ /**
28
+ * Add a file to the project.
29
+ * @throws {ReadCSSModuleFileError}
30
+ */
31
+ addFile(fileName: string): void;
32
+ /**
33
+ * Update a file in the project.
34
+ * @throws {ReadCSSModuleFileError}
35
+ */
36
+ updateFile(fileName: string): void;
37
+ /** Remove a file from the project. */
38
+ removeFile(fileName: string): void;
39
+ /**
40
+ * Get all diagnostics.
41
+ * Including three types of diagnostics: project diagnostics, syntactic diagnostics, and semantic diagnostics.
42
+ * - Project diagnostics: For example, it includes configuration errors in tsconfig.json or warnings when there are no target files.
43
+ * - Syntactic diagnostics: Syntax errors in CSS Module files.
44
+ * - Semantic diagnostics: Errors related to the use of imports and exports in CSS module files.
45
+ * If there are any project diagnostics or syntactic diagnostics, semantic diagnostics will be skipped.
46
+ */
47
+ getDiagnostics(): Diagnostic[];
48
+ /**
49
+ * Emit .d.ts files for all project files.
50
+ * @throws {WriteDtsFileError}
51
+ */
52
+ emitDtsFiles(): Promise<void>;
53
+ }
54
+
55
+ /**
56
+ * Create a Project instance.
57
+ * Project is like a facade that calls core operations such as loading settings, parsing CSS Module files, and performing checks.
58
+ * The parsing and checking results are cached, and methods are also provided to clear the cache when files change.
59
+ * @throws {TsConfigFileNotFoundError}
60
+ * @throws {ReadCSSModuleFileError}
61
+ */
62
+ export function createProject(args: ProjectArgs): Project {
63
+ const config = readConfigFile(args.project);
64
+
65
+ const getCanonicalFileName = (fileName: string) =>
66
+ ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
67
+ const moduleResolutionCache = ts.createModuleResolutionCache(
68
+ config.basePath,
69
+ getCanonicalFileName,
70
+ config.compilerOptions,
71
+ );
72
+ const resolver = createResolver(config.compilerOptions, moduleResolutionCache);
73
+ const matchesPattern = createMatchesPattern(config);
74
+
75
+ const cssModuleMap = new Map<string, CSSModule>();
76
+ const semanticDiagnosticsMap = new Map<string, Diagnostic[]>();
77
+ // Tracks whether .d.ts has been emitted after the last change
78
+ const emittedSet = new Set<string>();
79
+ const getCSSModule = (path: string) => cssModuleMap.get(path);
80
+ const exportBuilder = createExportBuilder({ getCSSModule, matchesPattern, resolver });
81
+
82
+ for (const fileName of getFileNamesByPattern(config)) {
83
+ // NOTE: Files may be deleted between executing `getFileNamesByPattern` and `tryParseCSSModule`.
84
+ // Therefore, `tryParseCSSModule` may return `undefined`.
85
+ const cssModule = tryParseCSSModule(fileName);
86
+ if (cssModule) cssModuleMap.set(fileName, cssModule);
87
+ }
88
+
89
+ /**
90
+ * @throws {ReadCSSModuleFileError}
91
+ */
92
+ function addFile(fileName: string) {
93
+ if (cssModuleMap.has(fileName)) return;
94
+
95
+ const cssModule = tryParseCSSModule(fileName);
96
+ if (!cssModule) return;
97
+ cssModuleMap.set(fileName, cssModule);
98
+
99
+ // TODO: Delete only the minimum amount of check stage cache
100
+ moduleResolutionCache.clear();
101
+ exportBuilder.clearCache();
102
+ semanticDiagnosticsMap.clear();
103
+ }
104
+
105
+ /**
106
+ * @throws {ReadCSSModuleFileError}
107
+ */
108
+ function updateFile(fileName: string) {
109
+ if (!cssModuleMap.has(fileName)) return;
110
+
111
+ const cssModule = tryParseCSSModule(fileName);
112
+ if (!cssModule) return;
113
+ cssModuleMap.set(fileName, cssModule);
114
+
115
+ // TODO: Delete only the minimum amount of check stage cache
116
+ exportBuilder.clearCache();
117
+ semanticDiagnosticsMap.clear();
118
+
119
+ emittedSet.delete(fileName);
120
+ }
121
+
122
+ function removeFile(fileName: string) {
123
+ if (!cssModuleMap.has(fileName)) return;
124
+
125
+ cssModuleMap.delete(fileName);
126
+
127
+ // TODO: Delete only the minimum amount of check stage cache
128
+ moduleResolutionCache.clear();
129
+ exportBuilder.clearCache();
130
+ semanticDiagnosticsMap.clear();
131
+
132
+ emittedSet.delete(fileName);
133
+ }
134
+
135
+ /**
136
+ * @throws {ReadCSSModuleFileError}
137
+ */
138
+ function tryParseCSSModule(fileName: string): CSSModule | undefined {
139
+ let text: string;
140
+ try {
141
+ // NOTE: We are not using asynchronous APIs for the following reasons:
142
+ //
143
+ // - Asynchronous APIs are slow in Node.js.
144
+ // - https://github.com/nodejs/performance/issues/151
145
+ // - Handling race conditions is cumbersome.
146
+ // - Using an asynchronous API makes `addFile` asynchronous too.
147
+ // - If `deleteFile` runs while `addFile` is executing, a race condition occurs.
148
+ // - Avoiding this requires something like a mutex. However, implementing that is cumbersome.
149
+ text = readFileSync(fileName, 'utf-8');
150
+ } catch (error) {
151
+ if (isNodeJSSystemError(error) && error.code === 'ENOENT') {
152
+ return undefined;
153
+ }
154
+ throw new ReadCSSModuleFileError(fileName, error);
155
+ }
156
+ return parseCSSModule(text, { fileName, includeSyntaxError: true, keyframes: config.keyframes });
157
+ }
158
+
159
+ function getDiagnostics(): Diagnostic[] {
160
+ const diagnostics: Diagnostic[] = [...getProjectDiagnostics(), ...getSyntacticDiagnostics()];
161
+ // If there are project or syntactic diagnostics, skip semantic diagnostics
162
+ if (diagnostics.length > 0) return diagnostics;
163
+ diagnostics.push(...getSemanticDiagnostics());
164
+ return diagnostics;
165
+ }
166
+
167
+ function getProjectDiagnostics() {
168
+ const diagnostics: Diagnostic[] = [];
169
+ diagnostics.push(...config.diagnostics);
170
+ if (config.enabled === undefined) {
171
+ diagnostics.push({
172
+ category: 'warning',
173
+ text: `"cmkOptions.enabled" will be required in a future version of css-modules-kit. Add \`"cmkOptions": { "enabled": true }\` to ${relative(config.basePath, config.configFileName)}. See https://github.com/mizdra/css-modules-kit/issues/289 for details.`,
174
+ });
175
+ }
176
+ if (cssModuleMap.size === 0) {
177
+ diagnostics.push({
178
+ category: 'error',
179
+ text: `The file specified in tsconfig.json not found.`,
180
+ });
181
+ }
182
+ return diagnostics;
183
+ }
184
+
185
+ function getSyntacticDiagnostics() {
186
+ return Array.from(cssModuleMap.values()).flatMap(({ diagnostics }) => diagnostics);
187
+ }
188
+
189
+ function getSemanticDiagnostics() {
190
+ const allDiagnostics: Diagnostic[] = [];
191
+ for (const cssModule of cssModuleMap.values()) {
192
+ let diagnostics = semanticDiagnosticsMap.get(cssModule.fileName);
193
+ if (!diagnostics) {
194
+ diagnostics = checkCSSModule(cssModule, {
195
+ config,
196
+ getExportRecord: (m) => exportBuilder.build(m),
197
+ matchesPattern,
198
+ resolver,
199
+ getCSSModule,
200
+ });
201
+ semanticDiagnosticsMap.set(cssModule.fileName, diagnostics);
202
+ }
203
+ allDiagnostics.push(...diagnostics);
204
+ }
205
+ return allDiagnostics;
206
+ }
207
+
208
+ /**
209
+ * @throws {WriteDtsFileError}
210
+ */
211
+ async function emitDtsFiles(): Promise<void> {
212
+ const promises: Promise<void>[] = [];
213
+ for (const cssModule of cssModuleMap.values()) {
214
+ if (emittedSet.has(cssModule.fileName)) continue;
215
+ const dts = generateDts(cssModule, { ...config, forTsPlugin: false });
216
+ promises.push(
217
+ writeDtsFile(dts.text, cssModule.fileName, {
218
+ outDir: config.dtsOutDir,
219
+ basePath: config.basePath,
220
+ arbitraryExtensions: config.arbitraryExtensions,
221
+ }).then(() => {
222
+ emittedSet.add(cssModule.fileName);
223
+ }),
224
+ );
225
+ }
226
+ await Promise.all(promises);
227
+ }
228
+
229
+ return {
230
+ config,
231
+ isWildcardMatchedFile: (fileName) => matchesPattern(fileName),
232
+ addFile,
233
+ updateFile,
234
+ removeFile,
235
+ getDiagnostics,
236
+ emitDtsFiles,
237
+ };
238
+ }
239
+
240
+ function isNodeJSSystemError(error: unknown): error is NodeJS.ErrnoException {
241
+ return typeof error === 'object' && error !== null && 'code' in error && typeof error.code === 'string';
242
+ }
package/src/runner.ts CHANGED
@@ -1,132 +1,182 @@
1
- import { readFile, rm } from 'node:fs/promises';
2
- import type {
3
- CMKConfig,
4
- CSSModule,
5
- Diagnostic,
6
- DiagnosticWithLocation,
7
- MatchesPattern,
8
- ParseCSSModuleResult,
9
- Resolver,
10
- } from '@css-modules-kit/core';
11
- import {
12
- checkCSSModule,
13
- createDts,
14
- createExportBuilder,
15
- createMatchesPattern,
16
- createResolver,
17
- getFileNamesByPattern,
18
- parseCSSModule,
19
- readConfigFile,
20
- } from '@css-modules-kit/core';
21
- import ts from 'typescript';
22
- import type { ParsedArgs } from './cli.js';
23
- import { writeDtsFile } from './dts-writer.js';
24
- import { ReadCSSModuleFileError } from './error.js';
1
+ import type { Stats } from 'node:fs';
2
+ import { rm } from 'node:fs/promises';
3
+ import chokidar, { type FSWatcher } from 'chokidar';
4
+ import { CMKDisabledError } from './error.js';
25
5
  import type { Logger } from './logger/logger.js';
6
+ import { createProject, type Project } from './project.js';
26
7
 
27
- /**
28
- * @throws {ReadCSSModuleFileError} When failed to read CSS Module file.
29
- */
30
- async function parseCSSModuleByFileName(fileName: string, config: CMKConfig): Promise<ParseCSSModuleResult> {
31
- let text: string;
32
- try {
33
- text = await readFile(fileName, 'utf-8');
34
- } catch (error) {
35
- throw new ReadCSSModuleFileError(fileName, error);
36
- }
37
- return parseCSSModule(text, { fileName, safe: false, keyframes: config.keyframes });
8
+ interface RunnerArgs {
9
+ project: string;
10
+ clean: boolean;
11
+ preserveWatchOutput: boolean;
12
+ }
13
+
14
+ export interface Watcher {
15
+ /** Exported for testing purposes */
16
+ project: Project;
17
+ close(): Promise<void>;
38
18
  }
39
19
 
40
20
  /**
21
+ * Run css-modules-kit .d.ts generation.
22
+ * @param project The absolute path to the project directory or the path to `tsconfig.json`.
23
+ * @throws {CMKDisabledError} When css-modules-kit is disabled.
24
+ * @throws {ReadCSSModuleFileError} When failed to read CSS Module file.
41
25
  * @throws {WriteDtsFileError}
26
+ * @returns Whether the process succeeded without errors.
42
27
  */
43
- async function writeDtsByCSSModule(
44
- cssModule: CSSModule,
45
- { dtsOutDir, basePath, arbitraryExtensions, namedExports, prioritizeNamedImports }: CMKConfig,
46
- resolver: Resolver,
47
- matchesPattern: MatchesPattern,
48
- ): Promise<void> {
49
- const dts = createDts(
50
- cssModule,
51
- { resolver, matchesPattern },
52
- { namedExports, prioritizeNamedImports, forTsPlugin: false },
53
- );
54
- await writeDtsFile(dts.text, cssModule.fileName, {
55
- outDir: dtsOutDir,
56
- basePath,
57
- arbitraryExtensions,
58
- });
28
+ export async function runCMK(args: RunnerArgs, logger: Logger): Promise<boolean> {
29
+ const project = createProject(args);
30
+ if (project.config.enabled === false) {
31
+ throw new CMKDisabledError(project.config);
32
+ }
33
+ if (args.clean) {
34
+ await rm(project.config.dtsOutDir, { recursive: true, force: true });
35
+ }
36
+ await project.emitDtsFiles();
37
+ const diagnostics = project.getDiagnostics();
38
+ if (diagnostics.length > 0) {
39
+ logger.logDiagnostics(diagnostics);
40
+ const hasErrors = diagnostics.some((d) => d.category === 'error');
41
+ return !hasErrors;
42
+ }
43
+ return true;
59
44
  }
60
45
 
61
46
  /**
62
- * Run css-modules-kit .d.ts generation.
47
+ * Run css-modules-kit .d.ts generation in watch mode.
48
+ *
49
+ * The promise resolves when the initial diagnostics report, emit, and watcher initialization are complete.
50
+ * Errors are reported through the logger.
51
+ *
52
+ * NOTE: For implementation simplicity, config file changes are not watched.
63
53
  * @param project The absolute path to the project directory or the path to `tsconfig.json`.
64
- * @throws {ReadCSSModuleFileError} When failed to read CSS Module file.
54
+ * @throws {CMKDisabledError} When css-modules-kit is disabled.
55
+ * @throws {TsConfigFileNotFoundError}
56
+ * @throws {ReadCSSModuleFileError}
65
57
  * @throws {WriteDtsFileError}
66
58
  */
67
- export async function runCMK(args: ParsedArgs, logger: Logger): Promise<void> {
68
- const config = readConfigFile(args.project);
69
- if (config.diagnostics.length > 0) {
70
- logger.logDiagnostics(config.diagnostics);
71
- // eslint-disable-next-line n/no-process-exit
72
- process.exit(1);
59
+ export async function runCMKInWatchMode(args: RunnerArgs, logger: Logger): Promise<Watcher> {
60
+ const fsWatchers: FSWatcher[] = [];
61
+ const project = createProject(args);
62
+ if (project.config.enabled === false) {
63
+ throw new CMKDisabledError(project.config);
73
64
  }
65
+ let emitAndReportDiagnosticsTimer: NodeJS.Timeout | undefined = undefined;
74
66
 
75
- const getCanonicalFileName = (fileName: string) =>
76
- ts.sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
77
- const moduleResolutionCache = ts.createModuleResolutionCache(
78
- config.basePath,
79
- getCanonicalFileName,
80
- config.compilerOptions,
81
- );
82
- const resolver = createResolver(config.compilerOptions, moduleResolutionCache);
83
- const matchesPattern = createMatchesPattern(config);
67
+ if (args.clean) {
68
+ await rm(project.config.dtsOutDir, { recursive: true, force: true });
69
+ }
70
+ await emitAndReportDiagnostics();
84
71
 
85
- const cssModuleMap = new Map<string, CSSModule>();
86
- const syntacticDiagnostics: DiagnosticWithLocation[] = [];
72
+ // Watch project files and report diagnostics on changes
73
+ const readyPromises: Promise<void>[] = [];
74
+ for (const wildcardDirectory of project.config.wildcardDirectories) {
75
+ const { promise, resolve } = promiseWithResolvers<void>();
76
+ readyPromises.push(promise);
77
+ fsWatchers.push(
78
+ chokidar
79
+ .watch(wildcardDirectory.fileName, {
80
+ ignored: (fileName: string, stats?: Stats) => {
81
+ // The ignored function is called twice for the same path. The first time with stats undefined,
82
+ // and the second time with stats provided.
83
+ // In the first call, we can't determine if the path is a directory or file.
84
+ // So we include it in the watch target considering it might be a directory.
85
+ if (!stats) return false;
87
86
 
88
- const fileNames = getFileNamesByPattern(config);
89
- if (fileNames.length === 0) {
90
- logger.logDiagnostics([
91
- {
92
- category: 'warning',
93
- text: `The file specified in tsconfig.json not found.`,
94
- },
95
- ]);
96
- return;
97
- }
98
- const parseResults = await Promise.all(fileNames.map(async (fileName) => parseCSSModuleByFileName(fileName, config)));
99
- for (const parseResult of parseResults) {
100
- cssModuleMap.set(parseResult.cssModule.fileName, parseResult.cssModule);
101
- syntacticDiagnostics.push(...parseResult.diagnostics);
87
+ // In the second call, we include directories or files that match wildcards in the watch target.
88
+ // However, `dtsOutDir` is excluded from the watch target.
89
+ if (stats.isDirectory()) {
90
+ return fileName === project.config.dtsOutDir;
91
+ } else {
92
+ return !project.isWildcardMatchedFile(fileName);
93
+ }
94
+ },
95
+ ignoreInitial: true,
96
+ ...(wildcardDirectory.recursive ? {} : { depth: 0 }),
97
+ })
98
+ .on('add', (fileName) => {
99
+ try {
100
+ project.addFile(fileName);
101
+ } catch (e) {
102
+ logger.logError(e);
103
+ return;
104
+ }
105
+ scheduleEmitAndReportDiagnostics();
106
+ })
107
+ .on('change', (fileName) => {
108
+ try {
109
+ project.updateFile(fileName);
110
+ } catch (e) {
111
+ logger.logError(e);
112
+ return;
113
+ }
114
+ scheduleEmitAndReportDiagnostics();
115
+ })
116
+ .on('unlink', (fileName: string) => {
117
+ project.removeFile(fileName);
118
+ scheduleEmitAndReportDiagnostics();
119
+ })
120
+ .on('error', (e) => logger.logError(e))
121
+ .on('ready', () => resolve()),
122
+ );
102
123
  }
124
+ await Promise.all(readyPromises);
103
125
 
104
- if (syntacticDiagnostics.length > 0) {
105
- logger.logDiagnostics(syntacticDiagnostics);
106
- // eslint-disable-next-line n/no-process-exit
107
- process.exit(1);
108
- }
126
+ function scheduleEmitAndReportDiagnostics() {
127
+ // Switching between git branches results in numerous file changes occurring rapidly.
128
+ // Reporting diagnostics for each file change would overwhelm users.
129
+ // Therefore, we batch the processing.
109
130
 
110
- const getCSSModule = (path: string) => cssModuleMap.get(path);
111
- const exportBuilder = createExportBuilder({ getCSSModule, matchesPattern, resolver });
112
- const semanticDiagnostics: Diagnostic[] = [];
113
- for (const { cssModule } of parseResults) {
114
- const diagnostics = checkCSSModule(cssModule, config, exportBuilder, matchesPattern, resolver, getCSSModule);
115
- semanticDiagnostics.push(...diagnostics);
131
+ if (emitAndReportDiagnosticsTimer !== undefined) clearTimeout(emitAndReportDiagnosticsTimer);
132
+
133
+ emitAndReportDiagnosticsTimer = setTimeout(() => {
134
+ emitAndReportDiagnosticsTimer = undefined;
135
+ emitAndReportDiagnostics().catch(logger.logError.bind(logger));
136
+ }, 250);
116
137
  }
117
138
 
118
- if (semanticDiagnostics.length > 0) {
119
- logger.logDiagnostics(semanticDiagnostics);
120
- // eslint-disable-next-line n/no-process-exit
121
- process.exit(1);
139
+ /**
140
+ * @throws {WriteDtsFileError}
141
+ */
142
+ async function emitAndReportDiagnostics() {
143
+ if (!args.preserveWatchOutput) {
144
+ logger.clearScreen();
145
+ }
146
+ await project.emitDtsFiles();
147
+ const diagnostics = project.getDiagnostics();
148
+ if (diagnostics.length > 0) {
149
+ logger.logDiagnostics(diagnostics);
150
+ }
151
+ const errorCount = diagnostics.filter((d) => d.category === 'error').length;
152
+ const warningCount = diagnostics.filter((d) => d.category === 'warning').length;
153
+ const warningPart = warningCount > 0 ? ` and ${warningCount} warning${warningCount === 1 ? '' : 's'}` : '';
154
+ logger.logMessage(
155
+ `Found ${errorCount} error${errorCount === 1 ? '' : 's'}${warningPart}. Watching for file changes.`,
156
+ { time: true },
157
+ );
158
+ if (args.preserveWatchOutput) {
159
+ logger.logMessage('');
160
+ }
122
161
  }
123
162
 
124
- if (args.clean) {
125
- await rm(config.dtsOutDir, { recursive: true, force: true });
163
+ async function close() {
164
+ await Promise.all(fsWatchers.map(async (watcher) => watcher.close()));
126
165
  }
127
- await Promise.all(
128
- parseResults.map(async (parseResult) =>
129
- writeDtsByCSSModule(parseResult.cssModule, config, resolver, matchesPattern),
130
- ),
131
- );
166
+
167
+ return { project, close };
168
+ }
169
+
170
+ function promiseWithResolvers<T>() {
171
+ let resolve;
172
+ let reject;
173
+ const promise = new Promise<T>((res, rej) => {
174
+ resolve = res;
175
+ reject = rej;
176
+ });
177
+ return {
178
+ promise,
179
+ resolve: resolve as unknown as (value: T) => void,
180
+ reject: reject as unknown as (reason?: unknown) => void,
181
+ };
132
182
  }
package/bin/cmk.mjs DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env node
2
- /* eslint-disable n/no-process-exit */
3
-
4
- import { SystemError } from '@css-modules-kit/core';
5
- import { createLogger, parseCLIArgs, printHelpText, printVersion, runCMK, shouldBePretty } from '../dist/index.js';
6
-
7
- const cwd = process.cwd();
8
- let logger = createLogger(cwd, shouldBePretty(undefined));
9
-
10
- try {
11
- const args = parseCLIArgs(process.argv.slice(2), cwd);
12
- logger = createLogger(cwd, shouldBePretty(args.pretty));
13
-
14
- if (args.help) {
15
- printHelpText();
16
- process.exit(0);
17
- } else if (args.version) {
18
- printVersion();
19
- process.exit(0);
20
- }
21
-
22
- await runCMK(args, logger);
23
- } catch (e) {
24
- if (e instanceof SystemError) {
25
- logger.logSystemError(e);
26
- process.exit(1);
27
- }
28
- throw e;
29
- }