@reliverse/dler 1.6.2 → 1.6.4

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 CHANGED
@@ -150,6 +150,52 @@ bun tools:agg # shortcut for:
150
150
  bun src/cli.ts tools --dev --tool agg --input src/libs/sdk/sdk-impl --out src/libs/sdk/sdk-mod.ts --recursive --named --strip src/libs/sdk
151
151
  ```
152
152
 
153
+ ### 1.5. `check`
154
+
155
+ checks your project for common issues and potential improvements. This command performs several types of checks:
156
+
157
+ - **File Extensions**: Validates that files have the correct extensions based on their location and module resolution strategy
158
+ - Enforces `.ts` files in source and JSR distributions
159
+ - Enforces `.js` files in NPM distributions
160
+ - Supports `.css` and `.json` files in all environments
161
+ - Adapts to your module resolution strategy (bundler/nodenext)
162
+
163
+ - **Path Extensions**: Ensures import statements use the correct file extensions
164
+ - Validates import paths match your module resolution strategy
165
+ - Checks for proper extension usage in import statements
166
+ - Supports both relative and absolute imports
167
+
168
+ - **Dependencies**: Identifies missing dependencies in your project
169
+ - Scans all source files for imports
170
+ - Compares against package.json
171
+ - Reports missing dependencies
172
+
173
+ ```bash
174
+ # Fully interactive mode (when no args provided)
175
+ dler check
176
+
177
+ # Mixed mode (some args provided, prompts for the rest)
178
+ dler check --directory src
179
+ dler check --checks file-extensions,path-extensions
180
+ dler check --strict
181
+
182
+ # Fully automated mode (all args provided)
183
+ dler check --directory src --checks file-extensions,path-extensions --strict
184
+
185
+ # Output in JSON format
186
+ dler check --json
187
+ ```
188
+
189
+ **arguments:**
190
+
191
+ - `--directory`: directory to check (src, dist-npm, dist-jsr, dist-libs/npm, dist-libs/jsr, or all)
192
+ - `--checks`: comma-separated list of checks to run (missing-dependencies, file-extensions, path-extensions)
193
+ - `--strict`: enable strict mode (requires explicit extensions)
194
+ - `--json`: output results in JSON format
195
+
196
+ **pro tip:**
197
+ the command will prompt you only for the arguments you haven't provided. for example, if you specify `--directory` but not `--checks`, it will only prompt you to select which checks to run.
198
+
153
199
  ### 2. `build`
154
200
 
155
201
  since dler is fully modular, build command is separated for its own build-in plugin as well.
@@ -164,7 +210,7 @@ not yet documented.
164
210
 
165
211
  ### 4. `deps`
166
212
 
167
- finds missing dependencies in your project by scanning your code for imports and comparing them to your `package.json`.
213
+ finds missing dependencies in your project by scanning your code for imports and comparing them to your `package.json`. This command is particularly useful for maintaining clean dependency lists and preventing runtime errors.
168
214
 
169
215
  **what it does:**
170
216
 
@@ -179,6 +225,7 @@ finds missing dependencies in your project by scanning your code for imports and
179
225
  - can also show all used dependencies (listed and missing)
180
226
  - optionally includes node.js built-in modules in the report
181
227
  - outputs results in a readable format or as json
228
+ - exits with error code 1 if missing dependencies are found
182
229
 
183
230
  **usage examples:**
184
231
 
@@ -34,7 +34,6 @@ declare const _default: import("@reliverse/rempts").Command<{
34
34
  sort: {
35
35
  description: string;
36
36
  type: "boolean";
37
- default: false;
38
37
  };
39
38
  header: {
40
39
  description: string;
@@ -43,12 +42,10 @@ declare const _default: import("@reliverse/rempts").Command<{
43
42
  verbose: {
44
43
  description: string;
45
44
  type: "boolean";
46
- default: false;
47
45
  };
48
46
  includeInternal: {
49
47
  description: string;
50
48
  type: "boolean";
51
- default: false;
52
49
  };
53
50
  internalMarker: {
54
51
  description: string;
@@ -58,7 +55,6 @@ declare const _default: import("@reliverse/rempts").Command<{
58
55
  override: {
59
56
  description: string;
60
57
  type: "boolean";
61
- default: false;
62
58
  };
63
59
  extensions: {
64
60
  description: string;
@@ -68,12 +64,10 @@ declare const _default: import("@reliverse/rempts").Command<{
68
64
  separateTypesFile: {
69
65
  description: string;
70
66
  type: "boolean";
71
- default: false;
72
67
  };
73
68
  typesOut: {
74
69
  description: string;
75
70
  type: "string";
76
- required: false;
77
71
  };
78
72
  }>;
79
73
  export default _default;
@@ -37,8 +37,7 @@ export default defineCommand({
37
37
  },
38
38
  sort: {
39
39
  description: "Sort aggregated lines alphabetically",
40
- type: "boolean",
41
- default: false
40
+ type: "boolean"
42
41
  },
43
42
  header: {
44
43
  description: "Add a header comment to the aggregator output",
@@ -46,13 +45,11 @@ export default defineCommand({
46
45
  },
47
46
  verbose: {
48
47
  description: "Enable verbose logging",
49
- type: "boolean",
50
- default: false
48
+ type: "boolean"
51
49
  },
52
50
  includeInternal: {
53
51
  description: "Include files marked as internal (starting with #)",
54
- type: "boolean",
55
- default: false
52
+ type: "boolean"
56
53
  },
57
54
  internalMarker: {
58
55
  description: "Marker for internal files (default: #)",
@@ -61,8 +58,7 @@ export default defineCommand({
61
58
  },
62
59
  override: {
63
60
  description: "Override entire file instead of updating only the aggregator block",
64
- type: "boolean",
65
- default: false
61
+ type: "boolean"
66
62
  },
67
63
  extensions: {
68
64
  description: "Comma-separated list of file extensions to process (default: .ts,.js,.mts,.cts,.mjs,.cjs)",
@@ -71,13 +67,11 @@ export default defineCommand({
71
67
  },
72
68
  separateTypesFile: {
73
69
  description: "Create a separate file for type exports",
74
- type: "boolean",
75
- default: false
70
+ type: "boolean"
76
71
  },
77
72
  typesOut: {
78
73
  description: "Output file path for types (used when separateTypesFile is true)",
79
- type: "string",
80
- required: false
74
+ type: "string"
81
75
  }
82
76
  }),
83
77
  async run({ args }) {
@@ -1,7 +1,21 @@
1
+ import type { CheckResult, FileCheckOptions } from "../../libs/sdk/sdk-types.js";
2
+ export declare function checkFileExtensions(options: FileCheckOptions): Promise<CheckResult>;
3
+ export declare function checkPathExtensions(options: FileCheckOptions): Promise<CheckResult>;
1
4
  declare const _default: import("@reliverse/rempts").Command<{
2
- exampleArg: {
5
+ directory: {
3
6
  type: "string";
4
- default: string;
7
+ description: string;
8
+ };
9
+ checks: {
10
+ type: "string";
11
+ description: string;
12
+ };
13
+ strict: {
14
+ type: "boolean";
15
+ description: string;
16
+ };
17
+ json: {
18
+ type: "boolean";
5
19
  description: string;
6
20
  };
7
21
  }>;
@@ -1,19 +1,496 @@
1
- import { defineCommand, defineArgs } from "@reliverse/rempts";
1
+ import { getFileImportsExports, extname, join } from "@reliverse/pathkit";
2
+ import fs from "@reliverse/relifso";
3
+ import { relinka } from "@reliverse/relinka";
4
+ import {
5
+ defineCommand,
6
+ selectPrompt,
7
+ multiselectPrompt,
8
+ confirmPrompt,
9
+ defineArgs
10
+ } from "@reliverse/rempts";
11
+ import { readTSConfig } from "pkg-types";
12
+ import { checkMissingDependencies } from "../deps/impl/wrapper.js";
13
+ import { IGNORE_PATTERNS } from "../../libs/sdk/sdk-impl/constants.js";
14
+ const ALLOWED_FILE_EXTENSIONS = {
15
+ src: ["", ".ts", ".css", ".json"],
16
+ // ✅ .ts files allowed in src
17
+ "dist-npm": ["", ".js", ".css", ".json"],
18
+ // ❌ no .ts files in npm dist
19
+ "dist-jsr": ["", ".ts", ".css", ".json"],
20
+ // ✅ .ts files allowed in jsr dist
21
+ "dist-libs/npm": ["", ".js", ".css", ".json"],
22
+ // ❌ no .ts files in npm libs
23
+ "dist-libs/jsr": ["", ".ts", ".css", ".json"]
24
+ // ✅ .ts files allowed in jsr libs
25
+ };
26
+ const STRICT_FILE_EXTENSIONS = {
27
+ src: [".ts", ".css", ".json"],
28
+ // ✅ .ts files required in src
29
+ "dist-npm": [".js", ".css", ".json"],
30
+ // ❌ no .ts files in npm dist
31
+ "dist-jsr": [".ts", ".css", ".json"],
32
+ // ✅ .ts files required in jsr dist
33
+ "dist-libs/npm": [".js", ".css", ".json"],
34
+ // ❌ no .ts files in npm libs
35
+ "dist-libs/jsr": [".ts", ".css", ".json"]
36
+ // ✅ .ts files required in jsr libs
37
+ };
38
+ const ALLOWED_IMPORT_EXTENSIONS = {
39
+ src: ["", ".js", ".css", ".json"],
40
+ // ❌ no .ts imports (use .js)
41
+ "dist-npm": ["", ".js", ".css", ".json"],
42
+ // ❌ no .ts imports
43
+ "dist-jsr": ["", ".ts", ".css", ".json"],
44
+ // ✅ .ts imports allowed
45
+ "dist-libs/npm": ["", ".js", ".css", ".json"],
46
+ // ❌ no .ts imports
47
+ "dist-libs/jsr": ["", ".ts", ".css", ".json"]
48
+ // ✅ .ts imports allowed
49
+ };
50
+ const STRICT_IMPORT_EXTENSIONS = {
51
+ src: [".js", ".css", ".json"],
52
+ // ❌ no .ts imports, no empty
53
+ "dist-npm": [".js", ".css", ".json"],
54
+ // ❌ no .ts imports
55
+ "dist-jsr": [".ts", ".css", ".json"],
56
+ // ✅ .ts imports required
57
+ "dist-libs/npm": [".js", ".css", ".json"],
58
+ // ❌ no .ts imports
59
+ "dist-libs/jsr": [".ts", ".css", ".json"]
60
+ // ✅ .ts imports required
61
+ };
62
+ async function validateDirectory(dir) {
63
+ try {
64
+ const stat = await fs.stat(dir);
65
+ return stat.isDirectory();
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+ function shouldIgnoreFile(filePath) {
71
+ const pathSegments = filePath.split("/");
72
+ return IGNORE_PATTERNS.some(
73
+ (pattern) => pathSegments.some((segment) => segment.includes(pattern))
74
+ );
75
+ }
76
+ async function getAllFiles(dir, onProgress) {
77
+ const results = [];
78
+ let fileCount = 0;
79
+ async function searchDirectory(directory) {
80
+ try {
81
+ const files = await fs.readdir(directory);
82
+ for (const file of files) {
83
+ const fullPath = join(directory, file);
84
+ if (shouldIgnoreFile(fullPath)) {
85
+ continue;
86
+ }
87
+ try {
88
+ const stat = await fs.stat(fullPath);
89
+ if (stat.isDirectory()) {
90
+ if (file === "templates") continue;
91
+ await searchDirectory(fullPath);
92
+ } else {
93
+ results.push(fullPath);
94
+ fileCount++;
95
+ onProgress?.(fileCount, fileCount, fullPath);
96
+ }
97
+ } catch {
98
+ relinka("warn", `skipping inaccessible file: ${fullPath}`);
99
+ }
100
+ }
101
+ } catch {
102
+ relinka("warn", `skipping inaccessible directory: ${directory}`);
103
+ }
104
+ }
105
+ if (!await validateDirectory(dir)) {
106
+ throw new Error(`directory "${dir}" does not exist or is not accessible`);
107
+ }
108
+ await searchDirectory(dir);
109
+ return results;
110
+ }
111
+ async function validateModuleResolution() {
112
+ try {
113
+ const tsconfig = await readTSConfig();
114
+ const moduleResolution = tsconfig?.compilerOptions?.moduleResolution;
115
+ if (!moduleResolution) {
116
+ throw new Error("moduleResolution is not specified in tsconfig.json");
117
+ }
118
+ if (moduleResolution !== "bundler" && moduleResolution !== "nodenext") {
119
+ throw new Error(
120
+ `unsupported moduleResolution: ${moduleResolution}. Only "bundler" and "nodenext" are supported`
121
+ );
122
+ }
123
+ return moduleResolution;
124
+ } catch (error) {
125
+ throw new Error(
126
+ `failed to read tsconfig.json: ${error instanceof Error ? error.message : "unknown error"}`
127
+ );
128
+ }
129
+ }
130
+ function getAllowedFileExtensions(directory, strict, moduleResolution) {
131
+ if (!strict) {
132
+ return ALLOWED_FILE_EXTENSIONS[directory];
133
+ }
134
+ if (moduleResolution === "bundler") {
135
+ return STRICT_FILE_EXTENSIONS[directory];
136
+ }
137
+ return STRICT_FILE_EXTENSIONS[directory];
138
+ }
139
+ function getAllowedImportExtensions(directory, strict) {
140
+ if (strict) {
141
+ return STRICT_IMPORT_EXTENSIONS[directory];
142
+ }
143
+ return ALLOWED_IMPORT_EXTENSIONS[directory];
144
+ }
145
+ export async function checkFileExtensions(options) {
146
+ const startTime = Date.now();
147
+ const issues = [];
148
+ const { directory, strict, moduleResolution, onProgress } = options;
149
+ const allowedExts = getAllowedFileExtensions(
150
+ directory,
151
+ strict,
152
+ moduleResolution
153
+ );
154
+ try {
155
+ const files = await getAllFiles(directory, onProgress);
156
+ const batchSize = 50;
157
+ const batches = [];
158
+ for (let i = 0; i < files.length; i += batchSize) {
159
+ batches.push(files.slice(i, i + batchSize));
160
+ }
161
+ for (const [batchIndex, batch] of batches.entries()) {
162
+ const batchPromises = batch.map(async (file, fileIndex) => {
163
+ const globalIndex = batchIndex * batchSize + fileIndex;
164
+ onProgress?.(globalIndex + 1, files.length, file);
165
+ const ext = extname(file);
166
+ if (!allowedExts.includes(ext)) {
167
+ let message = `file has disallowed extension "${ext}" (allowed: ${allowedExts.join(", ")})`;
168
+ if (ext === ".ts" && (directory === "dist-npm" || directory === "dist-libs/npm")) {
169
+ message = `typescript file found in javascript environment: ${file} (should be compiled to .js)`;
170
+ } else if (ext === ".js" && (directory === "src" || directory === "dist-jsr" || directory === "dist-libs/jsr")) {
171
+ message = `javascript file found in typescript environment: ${file} (should be .ts)`;
172
+ }
173
+ return {
174
+ file,
175
+ message,
176
+ type: "file-extension"
177
+ };
178
+ }
179
+ return null;
180
+ });
181
+ const batchResults = await Promise.all(batchPromises);
182
+ issues.push(...batchResults.filter(Boolean));
183
+ }
184
+ return {
185
+ success: issues.length === 0,
186
+ issues,
187
+ stats: {
188
+ filesChecked: files.length,
189
+ importsChecked: 0,
190
+ timeElapsed: Date.now() - startTime
191
+ }
192
+ };
193
+ } catch (error) {
194
+ throw new Error(
195
+ `failed to check file extensions: ${error instanceof Error ? error.message : "unknown error"}`
196
+ );
197
+ }
198
+ }
199
+ export async function checkPathExtensions(options) {
200
+ const startTime = Date.now();
201
+ const issues = [];
202
+ const { directory, strict, onProgress } = options;
203
+ const allowedExts = getAllowedImportExtensions(directory, strict);
204
+ try {
205
+ const files = await getAllFiles(directory);
206
+ let totalImports = 0;
207
+ const importableFiles = files.filter((file) => {
208
+ const ext = extname(file);
209
+ return [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext);
210
+ });
211
+ const batchSize = 20;
212
+ const batches = [];
213
+ for (let i = 0; i < importableFiles.length; i += batchSize) {
214
+ batches.push(importableFiles.slice(i, i + batchSize));
215
+ }
216
+ for (const [batchIndex, batch] of batches.entries()) {
217
+ const batchPromises = batch.map(async (file, fileIndex) => {
218
+ const globalIndex = batchIndex * batchSize + fileIndex;
219
+ onProgress?.(globalIndex + 1, importableFiles.length, file);
220
+ try {
221
+ const content = await fs.readFile(file, "utf-8");
222
+ const imports = getFileImportsExports(content, {
223
+ kind: "import",
224
+ pathTypes: ["relative", "alias"]
225
+ });
226
+ totalImports += imports.length;
227
+ const fileIssues = [];
228
+ for (const imp of imports) {
229
+ if (!imp.source) continue;
230
+ const ext = extname(imp.source);
231
+ if (!allowedExts.includes(ext)) {
232
+ const isTypeScriptImport = ext === ".ts";
233
+ const isJsEnvironment = directory === "src" || directory === "dist-npm" || directory === "dist-libs/npm";
234
+ let message;
235
+ if (isTypeScriptImport && isJsEnvironment) {
236
+ message = `import uses .ts extension in javascript environment: ${imp.source} (use .js extension instead)`;
237
+ } else {
238
+ message = `import has disallowed extension "${ext}": ${imp.source} (allowed: ${allowedExts.join(", ")})`;
239
+ }
240
+ fileIssues.push({
241
+ file,
242
+ message,
243
+ type: "path-extension",
244
+ line: getLineNumber(content, imp.start),
245
+ source: imp.source
246
+ });
247
+ }
248
+ }
249
+ return { issues: fileIssues, importCount: imports.length };
250
+ } catch {
251
+ relinka("warn", `skipping unreadable file: ${file}`);
252
+ return { issues: [], importCount: 0 };
253
+ }
254
+ });
255
+ const batchResults = await Promise.all(batchPromises);
256
+ for (const result of batchResults) {
257
+ issues.push(...result.issues);
258
+ totalImports += result.importCount;
259
+ }
260
+ }
261
+ return {
262
+ success: issues.length === 0,
263
+ issues,
264
+ stats: {
265
+ filesChecked: importableFiles.length,
266
+ importsChecked: totalImports,
267
+ timeElapsed: Date.now() - startTime
268
+ }
269
+ };
270
+ } catch (error) {
271
+ throw new Error(
272
+ `failed to check path extensions: ${error instanceof Error ? error.message : "unknown error"}`
273
+ );
274
+ }
275
+ }
276
+ function getLineNumber(content, position) {
277
+ return content.slice(0, position).split("\n").length;
278
+ }
279
+ function displayResults(checkType, directory, result) {
280
+ const { success, issues, stats } = result;
281
+ if (success) {
282
+ relinka("success", `\u2713 ${checkType} check passed for ${directory}`);
283
+ relinka(
284
+ "info",
285
+ ` files checked: ${stats.filesChecked}, imports: ${stats.importsChecked}, time: ${stats.timeElapsed}ms`
286
+ );
287
+ } else {
288
+ relinka(
289
+ "error",
290
+ `\u2717 ${checkType} check failed for ${directory} (${issues.length} issues)`
291
+ );
292
+ const fileIssues = issues.filter((i) => i.type === "file-extension");
293
+ const pathIssues = issues.filter((i) => i.type === "path-extension");
294
+ const missingDepIssues = issues.filter(
295
+ (i) => i.type === "missing-dependency"
296
+ );
297
+ const builtinIssues = issues.filter((i) => i.type === "builtin-module");
298
+ if (fileIssues.length > 0) {
299
+ relinka("error", ` file extension issues (${fileIssues.length}):`);
300
+ for (const issue of fileIssues.slice(0, 10)) {
301
+ relinka("error", ` ${issue.file}: ${issue.message}`);
302
+ }
303
+ if (fileIssues.length > 10) {
304
+ relinka("error", ` ... and ${fileIssues.length - 10} more`);
305
+ }
306
+ }
307
+ if (pathIssues.length > 0) {
308
+ relinka("error", ` import extension issues (${pathIssues.length}):`);
309
+ for (const issue of pathIssues.slice(0, 10)) {
310
+ relinka("error", ` ${issue.file}:${issue.line}: ${issue.message}`);
311
+ }
312
+ if (pathIssues.length > 10) {
313
+ relinka("error", ` ... and ${pathIssues.length - 10} more`);
314
+ }
315
+ }
316
+ if (missingDepIssues.length > 0) {
317
+ relinka("error", ` missing dependencies (${missingDepIssues.length}):`);
318
+ for (const issue of missingDepIssues.slice(0, 10)) {
319
+ relinka("error", ` ${issue.message}`);
320
+ }
321
+ if (missingDepIssues.length > 10) {
322
+ relinka("error", ` ... and ${missingDepIssues.length - 10} more`);
323
+ }
324
+ }
325
+ if (builtinIssues.length > 0) {
326
+ relinka("warn", ` builtin modules used (${builtinIssues.length}):`);
327
+ for (const issue of builtinIssues.slice(0, 10)) {
328
+ relinka("warn", ` ${issue.message}`);
329
+ }
330
+ if (builtinIssues.length > 10) {
331
+ relinka("warn", ` ... and ${builtinIssues.length - 10} more`);
332
+ }
333
+ }
334
+ relinka(
335
+ "info",
336
+ ` stats: ${stats.filesChecked} files, ${stats.importsChecked} imports, ${stats.timeElapsed}ms`
337
+ );
338
+ }
339
+ }
2
340
  export default defineCommand({
3
341
  meta: {
4
342
  name: "check",
5
343
  version: "1.0.0",
6
- description: "Describe what check command does."
344
+ description: "check your codebase source and dists for any issues."
7
345
  },
8
346
  args: defineArgs({
9
- exampleArg: {
347
+ directory: {
10
348
  type: "string",
11
- default: "defaultValue",
12
- description: "An example argument"
349
+ description: "directory to check (src, dist-npm, dist-jsr, dist-libs/npm, dist-libs/jsr, or all)"
350
+ },
351
+ checks: {
352
+ type: "string",
353
+ description: "comma-separated list of checks to run (missing-dependencies,file-extensions,path-extensions)"
354
+ },
355
+ strict: {
356
+ type: "boolean",
357
+ description: "enable strict mode (requires explicit extensions)"
358
+ },
359
+ json: {
360
+ type: "boolean",
361
+ description: "output results in JSON format"
13
362
  }
14
363
  }),
15
364
  async run({ args }) {
16
- console.log("Command 'check' executed.");
17
- console.log("Received args:", args);
365
+ relinka(
366
+ "info",
367
+ "this command checks your codebase for extension and dependency issues."
368
+ );
369
+ relinka(
370
+ "info",
371
+ "\u{1F4C1} file rules: .ts files allowed in src/jsr dirs, .js files in npm dirs"
372
+ );
373
+ relinka(
374
+ "info",
375
+ "\u{1F4E6} import rules: use .js imports in src/npm dirs, .ts imports in jsr dirs"
376
+ );
377
+ let moduleResolution;
378
+ try {
379
+ moduleResolution = await validateModuleResolution();
380
+ relinka("info", `using moduleResolution: ${moduleResolution}`);
381
+ } catch (error) {
382
+ relinka(
383
+ "error",
384
+ error instanceof Error ? error.message : "unknown error"
385
+ );
386
+ return;
387
+ }
388
+ let dir;
389
+ let checks;
390
+ let strict;
391
+ if (args.directory) {
392
+ dir = args.directory;
393
+ } else {
394
+ dir = await selectPrompt({
395
+ title: "select a directory to check",
396
+ options: [
397
+ { label: "all directories", value: "all" },
398
+ { label: "src (typescript source)", value: "src" },
399
+ { label: "dist-npm (compiled js)", value: "dist-npm" },
400
+ { label: "dist-jsr (typescript)", value: "dist-jsr" },
401
+ { label: "dist-libs/npm (compiled js)", value: "dist-libs/npm" },
402
+ { label: "dist-libs/jsr (typescript)", value: "dist-libs/jsr" }
403
+ ]
404
+ });
405
+ }
406
+ if (args.checks) {
407
+ checks = args.checks.split(",");
408
+ } else {
409
+ checks = await multiselectPrompt({
410
+ title: "select checks to run",
411
+ options: [
412
+ { label: "missing dependencies", value: "missing-dependencies" },
413
+ {
414
+ label: "file extensions (.ts/.js files)",
415
+ value: "file-extensions"
416
+ },
417
+ {
418
+ label: "import path extensions (.ts/.js imports)",
419
+ value: "path-extensions"
420
+ }
421
+ ]
422
+ });
423
+ }
424
+ if (args.strict !== void 0) {
425
+ strict = args.strict;
426
+ } else {
427
+ strict = await confirmPrompt({
428
+ title: "activate strict mode?",
429
+ content: "strict mode requires explicit extensions (no empty extensions). files: .ts in src/jsr dirs, .js in npm dirs. imports: .js in src/npm dirs, .ts in jsr dirs. templates folder is always exempt."
430
+ });
431
+ }
432
+ if (checks.length === 0) {
433
+ relinka("warn", "no checks selected, exiting...");
434
+ return;
435
+ }
436
+ const directories = dir === "all" ? [
437
+ "src",
438
+ "dist-npm",
439
+ "dist-jsr",
440
+ "dist-libs/npm",
441
+ "dist-libs/jsr"
442
+ ] : [dir];
443
+ for (const directory of directories) {
444
+ relinka("info", `
445
+ checking directory: ${directory}`);
446
+ const onProgress = (current, total) => {
447
+ if (current % 10 === 0 || current === total) {
448
+ process.stdout.write(`\r progress: ${current}/${total} files...`);
449
+ }
450
+ };
451
+ try {
452
+ if (checks.includes("missing-dependencies")) {
453
+ process.stdout.write(" checking missing dependencies...\n");
454
+ const result = await checkMissingDependencies({
455
+ directory,
456
+ strict: false,
457
+ // not used for deps check
458
+ moduleResolution,
459
+ // not used for deps check
460
+ onProgress
461
+ });
462
+ process.stdout.write("\r");
463
+ displayResults("missing dependencies", directory, result);
464
+ }
465
+ if (checks.includes("file-extensions")) {
466
+ process.stdout.write(" checking file extensions...\n");
467
+ const result = await checkFileExtensions({
468
+ directory,
469
+ strict,
470
+ moduleResolution,
471
+ onProgress
472
+ });
473
+ process.stdout.write("\r");
474
+ displayResults("file extensions", directory, result);
475
+ }
476
+ if (checks.includes("path-extensions")) {
477
+ process.stdout.write(" checking import path extensions...\n");
478
+ const result = await checkPathExtensions({
479
+ directory,
480
+ strict,
481
+ moduleResolution,
482
+ onProgress
483
+ });
484
+ process.stdout.write("\r");
485
+ displayResults("path extensions", directory, result);
486
+ }
487
+ } catch (error) {
488
+ relinka(
489
+ "error",
490
+ `failed to check ${directory}: ${error instanceof Error ? error.message : "unknown error"}`
491
+ );
492
+ }
493
+ }
494
+ relinka("success", "all checks completed!");
18
495
  }
19
496
  });