apdev-js 0.2.0 → 0.2.2
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 +1 -1
- package/dist/cli.js +184 -15
- package/dist/index.cjs +185 -14
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +182 -12
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# apdev
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
General-purpose development tools for TypeScript/JavaScript projects - character validation, circular import detection, and more.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ import { execFileSync } from "child_process";
|
|
|
15
15
|
import { Command } from "commander";
|
|
16
16
|
|
|
17
17
|
// src/check-chars.ts
|
|
18
|
-
import { readFileSync, existsSync } from "fs";
|
|
18
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
19
19
|
import { extname, dirname, join, sep } from "path";
|
|
20
20
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
21
21
|
function getCharsetsDir() {
|
|
@@ -259,10 +259,164 @@ function checkFile(filePath, maxProblems = 5, extraRanges, dangerousMap) {
|
|
|
259
259
|
}
|
|
260
260
|
return problems;
|
|
261
261
|
}
|
|
262
|
+
var SKIP_SUFFIXES = /* @__PURE__ */ new Set([
|
|
263
|
+
// Bytecode
|
|
264
|
+
".pyc",
|
|
265
|
+
".pyo",
|
|
266
|
+
// Images
|
|
267
|
+
".png",
|
|
268
|
+
".jpg",
|
|
269
|
+
".jpeg",
|
|
270
|
+
".gif",
|
|
271
|
+
".bmp",
|
|
272
|
+
".ico",
|
|
273
|
+
".svg",
|
|
274
|
+
".webp",
|
|
275
|
+
// Fonts
|
|
276
|
+
".ttf",
|
|
277
|
+
".otf",
|
|
278
|
+
".woff",
|
|
279
|
+
".woff2",
|
|
280
|
+
".eot",
|
|
281
|
+
// Archives
|
|
282
|
+
".zip",
|
|
283
|
+
".tar",
|
|
284
|
+
".gz",
|
|
285
|
+
".bz2",
|
|
286
|
+
".xz",
|
|
287
|
+
".7z",
|
|
288
|
+
// Compiled / binary
|
|
289
|
+
".so",
|
|
290
|
+
".dylib",
|
|
291
|
+
".dll",
|
|
292
|
+
".exe",
|
|
293
|
+
".o",
|
|
294
|
+
".a",
|
|
295
|
+
".whl",
|
|
296
|
+
".egg",
|
|
297
|
+
// Media
|
|
298
|
+
".mp3",
|
|
299
|
+
".mp4",
|
|
300
|
+
".wav",
|
|
301
|
+
".avi",
|
|
302
|
+
".mov",
|
|
303
|
+
".flac",
|
|
304
|
+
".ogg",
|
|
305
|
+
// Documents
|
|
306
|
+
".pdf",
|
|
307
|
+
".doc",
|
|
308
|
+
".docx",
|
|
309
|
+
".xls",
|
|
310
|
+
".xlsx",
|
|
311
|
+
".ppt",
|
|
312
|
+
".pptx",
|
|
313
|
+
// Data
|
|
314
|
+
".db",
|
|
315
|
+
".sqlite",
|
|
316
|
+
".sqlite3",
|
|
317
|
+
".pickle",
|
|
318
|
+
".pkl"
|
|
319
|
+
]);
|
|
320
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
321
|
+
"__pycache__",
|
|
322
|
+
"node_modules",
|
|
323
|
+
".git",
|
|
324
|
+
".venv",
|
|
325
|
+
"venv",
|
|
326
|
+
".tox",
|
|
327
|
+
".mypy_cache",
|
|
328
|
+
".pytest_cache",
|
|
329
|
+
".ruff_cache",
|
|
330
|
+
"dist",
|
|
331
|
+
"build"
|
|
332
|
+
]);
|
|
333
|
+
var DEFAULT_DIRS = ["src", "tests", "examples"];
|
|
334
|
+
var DEFAULT_GLOBS = ["*.md", "*.yml", "*.yaml", "*.json", ".gitignore"];
|
|
335
|
+
function walkDir(directory) {
|
|
336
|
+
const files = [];
|
|
337
|
+
let entries;
|
|
338
|
+
try {
|
|
339
|
+
entries = readdirSync(directory).sort();
|
|
340
|
+
} catch {
|
|
341
|
+
return files;
|
|
342
|
+
}
|
|
343
|
+
for (const name of entries) {
|
|
344
|
+
if (name.startsWith(".")) continue;
|
|
345
|
+
const fullPath = join(directory, name);
|
|
346
|
+
let stat;
|
|
347
|
+
try {
|
|
348
|
+
stat = statSync(fullPath);
|
|
349
|
+
} catch {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (stat.isDirectory()) {
|
|
353
|
+
if (SKIP_DIRS.has(name) || name.endsWith(".egg-info")) continue;
|
|
354
|
+
files.push(...walkDir(fullPath));
|
|
355
|
+
} else if (stat.isFile()) {
|
|
356
|
+
if (SKIP_SUFFIXES.has(extname(name).toLowerCase())) continue;
|
|
357
|
+
files.push(fullPath);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return files;
|
|
361
|
+
}
|
|
362
|
+
function defaultProjectFiles() {
|
|
363
|
+
const cwd = process.cwd();
|
|
364
|
+
const files = [];
|
|
365
|
+
for (const dirname3 of DEFAULT_DIRS) {
|
|
366
|
+
const d = join(cwd, dirname3);
|
|
367
|
+
if (existsSync(d) && statSync(d).isDirectory()) {
|
|
368
|
+
files.push(...walkDir(d));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
for (const pattern of DEFAULT_GLOBS) {
|
|
372
|
+
if (pattern.startsWith("*.")) {
|
|
373
|
+
const suffix = pattern.slice(1);
|
|
374
|
+
try {
|
|
375
|
+
for (const name of readdirSync(cwd).sort()) {
|
|
376
|
+
if (name.endsWith(suffix) && statSync(join(cwd, name)).isFile()) {
|
|
377
|
+
files.push(join(cwd, name));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} catch {
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
const fullPath = join(cwd, pattern);
|
|
384
|
+
if (existsSync(fullPath) && statSync(fullPath).isFile()) {
|
|
385
|
+
files.push(fullPath);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return files;
|
|
390
|
+
}
|
|
391
|
+
function resolvePaths(paths) {
|
|
392
|
+
if (paths.length === 0) {
|
|
393
|
+
return defaultProjectFiles();
|
|
394
|
+
}
|
|
395
|
+
const result = [];
|
|
396
|
+
for (const p of paths) {
|
|
397
|
+
try {
|
|
398
|
+
if (statSync(p).isDirectory()) {
|
|
399
|
+
result.push(...walkDir(p));
|
|
400
|
+
} else {
|
|
401
|
+
result.push(p);
|
|
402
|
+
}
|
|
403
|
+
} catch {
|
|
404
|
+
result.push(p);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return result;
|
|
408
|
+
}
|
|
262
409
|
function checkPaths(paths, extraRanges, dangerousMap) {
|
|
410
|
+
const resolved = resolvePaths(paths);
|
|
411
|
+
if (resolved.length === 0) {
|
|
412
|
+
console.log("No files to check.");
|
|
413
|
+
return 0;
|
|
414
|
+
}
|
|
263
415
|
let hasError = false;
|
|
264
|
-
|
|
416
|
+
let checked = 0;
|
|
417
|
+
for (const path2 of resolved) {
|
|
265
418
|
const problems = checkFile(path2, 5, extraRanges, dangerousMap);
|
|
419
|
+
checked++;
|
|
266
420
|
if (problems.length > 0) {
|
|
267
421
|
hasError = true;
|
|
268
422
|
console.log(`
|
|
@@ -272,13 +426,27 @@ ${path2} contains illegal characters:`);
|
|
|
272
426
|
}
|
|
273
427
|
}
|
|
274
428
|
}
|
|
429
|
+
if (!hasError) {
|
|
430
|
+
console.log(`All ${checked} files passed.`);
|
|
431
|
+
}
|
|
275
432
|
return hasError ? 1 : 0;
|
|
276
433
|
}
|
|
277
434
|
|
|
278
435
|
// src/check-imports.ts
|
|
279
|
-
import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
436
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
280
437
|
import { join as join2, relative, sep as sep2, extname as extname2, basename } from "path";
|
|
281
|
-
|
|
438
|
+
var _ts;
|
|
439
|
+
async function loadTS() {
|
|
440
|
+
if (_ts) return _ts;
|
|
441
|
+
try {
|
|
442
|
+
_ts = (await import("typescript")).default;
|
|
443
|
+
return _ts;
|
|
444
|
+
} catch {
|
|
445
|
+
throw new Error(
|
|
446
|
+
"The 'typescript' package is required for circular import detection.\nInstall it with: npm install -g typescript\nOr locally: npm install -D typescript"
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
282
450
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
283
451
|
".ts",
|
|
284
452
|
".tsx",
|
|
@@ -307,7 +475,8 @@ function fileToModule(filePath, srcDir) {
|
|
|
307
475
|
}
|
|
308
476
|
return parts.join(".");
|
|
309
477
|
}
|
|
310
|
-
function extractImports(source, fileName) {
|
|
478
|
+
async function extractImports(source, fileName) {
|
|
479
|
+
const ts = await loadTS();
|
|
311
480
|
const imports = /* @__PURE__ */ new Set();
|
|
312
481
|
const sourceFile = ts.createSourceFile(
|
|
313
482
|
fileName,
|
|
@@ -365,13 +534,13 @@ function resolveImports(rawImports, basePackage, currentModule, isPackage) {
|
|
|
365
534
|
function findSourceFiles(dir) {
|
|
366
535
|
const results = [];
|
|
367
536
|
function walk(d) {
|
|
368
|
-
const entries =
|
|
537
|
+
const entries = readdirSync2(d);
|
|
369
538
|
for (const entry of entries) {
|
|
370
539
|
if (entry === "node_modules" || entry === "dist" || entry === ".git") {
|
|
371
540
|
continue;
|
|
372
541
|
}
|
|
373
542
|
const full = join2(d, entry);
|
|
374
|
-
const stat =
|
|
543
|
+
const stat = statSync2(full);
|
|
375
544
|
if (stat.isDirectory()) {
|
|
376
545
|
walk(full);
|
|
377
546
|
} else if (SUPPORTED_EXTENSIONS.has(extname2(entry))) {
|
|
@@ -382,7 +551,7 @@ function findSourceFiles(dir) {
|
|
|
382
551
|
walk(dir);
|
|
383
552
|
return results;
|
|
384
553
|
}
|
|
385
|
-
function buildDependencyGraph(srcDir, basePackage) {
|
|
554
|
+
async function buildDependencyGraph(srcDir, basePackage) {
|
|
386
555
|
const graph = /* @__PURE__ */ new Map();
|
|
387
556
|
const files = findSourceFiles(srcDir);
|
|
388
557
|
for (const file of files) {
|
|
@@ -397,7 +566,7 @@ function buildDependencyGraph(srcDir, basePackage) {
|
|
|
397
566
|
}
|
|
398
567
|
let rawImports;
|
|
399
568
|
try {
|
|
400
|
-
rawImports = extractImports(source, file);
|
|
569
|
+
rawImports = await extractImports(source, file);
|
|
401
570
|
} catch (e) {
|
|
402
571
|
console.error(`Warning: could not parse ${file}: ${e}`);
|
|
403
572
|
continue;
|
|
@@ -456,10 +625,10 @@ function findCycles(graph) {
|
|
|
456
625
|
}
|
|
457
626
|
return unique;
|
|
458
627
|
}
|
|
459
|
-
function checkCircularImports(srcDir, basePackage) {
|
|
628
|
+
async function checkCircularImports(srcDir, basePackage) {
|
|
460
629
|
let stat;
|
|
461
630
|
try {
|
|
462
|
-
stat =
|
|
631
|
+
stat = statSync2(srcDir);
|
|
463
632
|
} catch {
|
|
464
633
|
console.error(`Error: ${srcDir}/ directory not found`);
|
|
465
634
|
return 1;
|
|
@@ -468,7 +637,7 @@ function checkCircularImports(srcDir, basePackage) {
|
|
|
468
637
|
console.error(`Error: ${srcDir}/ is not a directory`);
|
|
469
638
|
return 1;
|
|
470
639
|
}
|
|
471
|
-
const graph = buildDependencyGraph(srcDir, basePackage);
|
|
640
|
+
const graph = await buildDependencyGraph(srcDir, basePackage);
|
|
472
641
|
console.log(`Scanned ${graph.size} modules`);
|
|
473
642
|
const cycles = findCycles(graph);
|
|
474
643
|
if (cycles.length > 0) {
|
|
@@ -526,7 +695,7 @@ function getReleaseScript() {
|
|
|
526
695
|
function buildProgram() {
|
|
527
696
|
const program2 = new Command();
|
|
528
697
|
program2.name("apdev").description("Shared development tools for TypeScript/JavaScript projects").version(getVersion());
|
|
529
|
-
program2.command("check-chars").description("Validate files contain only allowed characters").argument("
|
|
698
|
+
program2.command("check-chars").description("Validate files contain only allowed characters").argument("[files...]", "Files or directories to check (defaults to src/, tests/, examples/ and config files)").option("--charset <name>", "Extra charset preset (repeatable)", collect, []).option("--charset-file <path>", "Custom charset JSON file (repeatable)", collect, []).action((files, opts) => {
|
|
530
699
|
let charsetNames = opts.charset;
|
|
531
700
|
let charsetFiles = opts.charsetFile;
|
|
532
701
|
if (charsetNames.length === 0 && charsetFiles.length === 0) {
|
|
@@ -548,7 +717,7 @@ function buildProgram() {
|
|
|
548
717
|
const code = checkPaths(resolved, ranges, dangerous);
|
|
549
718
|
process.exit(code);
|
|
550
719
|
});
|
|
551
|
-
program2.command("check-imports").description("Detect circular imports in a JS/TS package").option("--package <name>", "Base package name (e.g. mylib). Reads from package.json apdev config if omitted.").option("--src-dir <dir>", "Source directory containing the package (default: src)").action((opts) => {
|
|
720
|
+
program2.command("check-imports").description("Detect circular imports in a JS/TS package").option("--package <name>", "Base package name (e.g. mylib). Reads from package.json apdev config if omitted.").option("--src-dir <dir>", "Source directory containing the package (default: src)").action(async (opts) => {
|
|
552
721
|
const config = loadConfig();
|
|
553
722
|
const basePackage = opts.package ?? config["base_package"];
|
|
554
723
|
const srcDir = opts.srcDir ?? config["src_dir"] ?? "src";
|
|
@@ -558,7 +727,7 @@ function buildProgram() {
|
|
|
558
727
|
);
|
|
559
728
|
process.exit(1);
|
|
560
729
|
}
|
|
561
|
-
const code = checkCircularImports(resolve(srcDir), basePackage);
|
|
730
|
+
const code = await checkCircularImports(resolve(srcDir), basePackage);
|
|
562
731
|
process.exit(code);
|
|
563
732
|
});
|
|
564
733
|
program2.command("release").description("Interactive release automation (build, tag, GitHub release, npm publish)").option("-y, --yes", "Auto-accept all defaults (silent mode)").argument("[version]", "Version to release (auto-detected from package.json if omitted)").action((version, opts) => {
|
package/dist/index.cjs
CHANGED
|
@@ -41,6 +41,7 @@ __export(src_exports, {
|
|
|
41
41
|
loadCharset: () => loadCharset,
|
|
42
42
|
loadConfig: () => loadConfig,
|
|
43
43
|
resolveCharsets: () => resolveCharsets,
|
|
44
|
+
resolvePaths: () => resolvePaths,
|
|
44
45
|
version: () => version
|
|
45
46
|
});
|
|
46
47
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -304,10 +305,164 @@ function checkFile(filePath, maxProblems = 5, extraRanges, dangerousMap) {
|
|
|
304
305
|
}
|
|
305
306
|
return problems;
|
|
306
307
|
}
|
|
308
|
+
var SKIP_SUFFIXES = /* @__PURE__ */ new Set([
|
|
309
|
+
// Bytecode
|
|
310
|
+
".pyc",
|
|
311
|
+
".pyo",
|
|
312
|
+
// Images
|
|
313
|
+
".png",
|
|
314
|
+
".jpg",
|
|
315
|
+
".jpeg",
|
|
316
|
+
".gif",
|
|
317
|
+
".bmp",
|
|
318
|
+
".ico",
|
|
319
|
+
".svg",
|
|
320
|
+
".webp",
|
|
321
|
+
// Fonts
|
|
322
|
+
".ttf",
|
|
323
|
+
".otf",
|
|
324
|
+
".woff",
|
|
325
|
+
".woff2",
|
|
326
|
+
".eot",
|
|
327
|
+
// Archives
|
|
328
|
+
".zip",
|
|
329
|
+
".tar",
|
|
330
|
+
".gz",
|
|
331
|
+
".bz2",
|
|
332
|
+
".xz",
|
|
333
|
+
".7z",
|
|
334
|
+
// Compiled / binary
|
|
335
|
+
".so",
|
|
336
|
+
".dylib",
|
|
337
|
+
".dll",
|
|
338
|
+
".exe",
|
|
339
|
+
".o",
|
|
340
|
+
".a",
|
|
341
|
+
".whl",
|
|
342
|
+
".egg",
|
|
343
|
+
// Media
|
|
344
|
+
".mp3",
|
|
345
|
+
".mp4",
|
|
346
|
+
".wav",
|
|
347
|
+
".avi",
|
|
348
|
+
".mov",
|
|
349
|
+
".flac",
|
|
350
|
+
".ogg",
|
|
351
|
+
// Documents
|
|
352
|
+
".pdf",
|
|
353
|
+
".doc",
|
|
354
|
+
".docx",
|
|
355
|
+
".xls",
|
|
356
|
+
".xlsx",
|
|
357
|
+
".ppt",
|
|
358
|
+
".pptx",
|
|
359
|
+
// Data
|
|
360
|
+
".db",
|
|
361
|
+
".sqlite",
|
|
362
|
+
".sqlite3",
|
|
363
|
+
".pickle",
|
|
364
|
+
".pkl"
|
|
365
|
+
]);
|
|
366
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
367
|
+
"__pycache__",
|
|
368
|
+
"node_modules",
|
|
369
|
+
".git",
|
|
370
|
+
".venv",
|
|
371
|
+
"venv",
|
|
372
|
+
".tox",
|
|
373
|
+
".mypy_cache",
|
|
374
|
+
".pytest_cache",
|
|
375
|
+
".ruff_cache",
|
|
376
|
+
"dist",
|
|
377
|
+
"build"
|
|
378
|
+
]);
|
|
379
|
+
var DEFAULT_DIRS = ["src", "tests", "examples"];
|
|
380
|
+
var DEFAULT_GLOBS = ["*.md", "*.yml", "*.yaml", "*.json", ".gitignore"];
|
|
381
|
+
function walkDir(directory) {
|
|
382
|
+
const files = [];
|
|
383
|
+
let entries;
|
|
384
|
+
try {
|
|
385
|
+
entries = (0, import_node_fs.readdirSync)(directory).sort();
|
|
386
|
+
} catch {
|
|
387
|
+
return files;
|
|
388
|
+
}
|
|
389
|
+
for (const name of entries) {
|
|
390
|
+
if (name.startsWith(".")) continue;
|
|
391
|
+
const fullPath = (0, import_node_path.join)(directory, name);
|
|
392
|
+
let stat;
|
|
393
|
+
try {
|
|
394
|
+
stat = (0, import_node_fs.statSync)(fullPath);
|
|
395
|
+
} catch {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (stat.isDirectory()) {
|
|
399
|
+
if (SKIP_DIRS.has(name) || name.endsWith(".egg-info")) continue;
|
|
400
|
+
files.push(...walkDir(fullPath));
|
|
401
|
+
} else if (stat.isFile()) {
|
|
402
|
+
if (SKIP_SUFFIXES.has((0, import_node_path.extname)(name).toLowerCase())) continue;
|
|
403
|
+
files.push(fullPath);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return files;
|
|
407
|
+
}
|
|
408
|
+
function defaultProjectFiles() {
|
|
409
|
+
const cwd = process.cwd();
|
|
410
|
+
const files = [];
|
|
411
|
+
for (const dirname3 of DEFAULT_DIRS) {
|
|
412
|
+
const d = (0, import_node_path.join)(cwd, dirname3);
|
|
413
|
+
if ((0, import_node_fs.existsSync)(d) && (0, import_node_fs.statSync)(d).isDirectory()) {
|
|
414
|
+
files.push(...walkDir(d));
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
for (const pattern of DEFAULT_GLOBS) {
|
|
418
|
+
if (pattern.startsWith("*.")) {
|
|
419
|
+
const suffix = pattern.slice(1);
|
|
420
|
+
try {
|
|
421
|
+
for (const name of (0, import_node_fs.readdirSync)(cwd).sort()) {
|
|
422
|
+
if (name.endsWith(suffix) && (0, import_node_fs.statSync)((0, import_node_path.join)(cwd, name)).isFile()) {
|
|
423
|
+
files.push((0, import_node_path.join)(cwd, name));
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
} catch {
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
const fullPath = (0, import_node_path.join)(cwd, pattern);
|
|
430
|
+
if ((0, import_node_fs.existsSync)(fullPath) && (0, import_node_fs.statSync)(fullPath).isFile()) {
|
|
431
|
+
files.push(fullPath);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return files;
|
|
436
|
+
}
|
|
437
|
+
function resolvePaths(paths) {
|
|
438
|
+
if (paths.length === 0) {
|
|
439
|
+
return defaultProjectFiles();
|
|
440
|
+
}
|
|
441
|
+
const result = [];
|
|
442
|
+
for (const p of paths) {
|
|
443
|
+
try {
|
|
444
|
+
if ((0, import_node_fs.statSync)(p).isDirectory()) {
|
|
445
|
+
result.push(...walkDir(p));
|
|
446
|
+
} else {
|
|
447
|
+
result.push(p);
|
|
448
|
+
}
|
|
449
|
+
} catch {
|
|
450
|
+
result.push(p);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return result;
|
|
454
|
+
}
|
|
307
455
|
function checkPaths(paths, extraRanges, dangerousMap) {
|
|
456
|
+
const resolved = resolvePaths(paths);
|
|
457
|
+
if (resolved.length === 0) {
|
|
458
|
+
console.log("No files to check.");
|
|
459
|
+
return 0;
|
|
460
|
+
}
|
|
308
461
|
let hasError = false;
|
|
309
|
-
|
|
462
|
+
let checked = 0;
|
|
463
|
+
for (const path of resolved) {
|
|
310
464
|
const problems = checkFile(path, 5, extraRanges, dangerousMap);
|
|
465
|
+
checked++;
|
|
311
466
|
if (problems.length > 0) {
|
|
312
467
|
hasError = true;
|
|
313
468
|
console.log(`
|
|
@@ -317,13 +472,27 @@ ${path} contains illegal characters:`);
|
|
|
317
472
|
}
|
|
318
473
|
}
|
|
319
474
|
}
|
|
475
|
+
if (!hasError) {
|
|
476
|
+
console.log(`All ${checked} files passed.`);
|
|
477
|
+
}
|
|
320
478
|
return hasError ? 1 : 0;
|
|
321
479
|
}
|
|
322
480
|
|
|
323
481
|
// src/check-imports.ts
|
|
324
482
|
var import_node_fs2 = require("fs");
|
|
325
483
|
var import_node_path2 = require("path");
|
|
326
|
-
var
|
|
484
|
+
var _ts;
|
|
485
|
+
async function loadTS() {
|
|
486
|
+
if (_ts) return _ts;
|
|
487
|
+
try {
|
|
488
|
+
_ts = (await import("typescript")).default;
|
|
489
|
+
return _ts;
|
|
490
|
+
} catch {
|
|
491
|
+
throw new Error(
|
|
492
|
+
"The 'typescript' package is required for circular import detection.\nInstall it with: npm install -g typescript\nOr locally: npm install -D typescript"
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
327
496
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
328
497
|
".ts",
|
|
329
498
|
".tsx",
|
|
@@ -352,26 +521,27 @@ function fileToModule(filePath, srcDir) {
|
|
|
352
521
|
}
|
|
353
522
|
return parts.join(".");
|
|
354
523
|
}
|
|
355
|
-
function extractImports(source, fileName) {
|
|
524
|
+
async function extractImports(source, fileName) {
|
|
525
|
+
const ts = await loadTS();
|
|
356
526
|
const imports = /* @__PURE__ */ new Set();
|
|
357
|
-
const sourceFile =
|
|
527
|
+
const sourceFile = ts.createSourceFile(
|
|
358
528
|
fileName,
|
|
359
529
|
source,
|
|
360
|
-
|
|
530
|
+
ts.ScriptTarget.Latest,
|
|
361
531
|
true,
|
|
362
|
-
fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ?
|
|
532
|
+
fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : void 0
|
|
363
533
|
);
|
|
364
534
|
function visit(node) {
|
|
365
|
-
if (
|
|
535
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
366
536
|
imports.add(node.moduleSpecifier.text);
|
|
367
537
|
}
|
|
368
|
-
if (
|
|
538
|
+
if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
369
539
|
imports.add(node.moduleSpecifier.text);
|
|
370
540
|
}
|
|
371
|
-
if (
|
|
541
|
+
if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.Identifier && node.expression.text === "require" && node.arguments.length === 1 && ts.isStringLiteral(node.arguments[0])) {
|
|
372
542
|
imports.add(node.arguments[0].text);
|
|
373
543
|
}
|
|
374
|
-
|
|
544
|
+
ts.forEachChild(node, visit);
|
|
375
545
|
}
|
|
376
546
|
visit(sourceFile);
|
|
377
547
|
return imports;
|
|
@@ -427,7 +597,7 @@ function findSourceFiles(dir) {
|
|
|
427
597
|
walk(dir);
|
|
428
598
|
return results;
|
|
429
599
|
}
|
|
430
|
-
function buildDependencyGraph(srcDir, basePackage) {
|
|
600
|
+
async function buildDependencyGraph(srcDir, basePackage) {
|
|
431
601
|
const graph = /* @__PURE__ */ new Map();
|
|
432
602
|
const files = findSourceFiles(srcDir);
|
|
433
603
|
for (const file of files) {
|
|
@@ -442,7 +612,7 @@ function buildDependencyGraph(srcDir, basePackage) {
|
|
|
442
612
|
}
|
|
443
613
|
let rawImports;
|
|
444
614
|
try {
|
|
445
|
-
rawImports = extractImports(source, file);
|
|
615
|
+
rawImports = await extractImports(source, file);
|
|
446
616
|
} catch (e) {
|
|
447
617
|
console.error(`Warning: could not parse ${file}: ${e}`);
|
|
448
618
|
continue;
|
|
@@ -501,7 +671,7 @@ function findCycles(graph) {
|
|
|
501
671
|
}
|
|
502
672
|
return unique;
|
|
503
673
|
}
|
|
504
|
-
function checkCircularImports(srcDir, basePackage) {
|
|
674
|
+
async function checkCircularImports(srcDir, basePackage) {
|
|
505
675
|
let stat;
|
|
506
676
|
try {
|
|
507
677
|
stat = (0, import_node_fs2.statSync)(srcDir);
|
|
@@ -513,7 +683,7 @@ function checkCircularImports(srcDir, basePackage) {
|
|
|
513
683
|
console.error(`Error: ${srcDir}/ is not a directory`);
|
|
514
684
|
return 1;
|
|
515
685
|
}
|
|
516
|
-
const graph = buildDependencyGraph(srcDir, basePackage);
|
|
686
|
+
const graph = await buildDependencyGraph(srcDir, basePackage);
|
|
517
687
|
console.log(`Scanned ${graph.size} modules`);
|
|
518
688
|
const cycles = findCycles(graph);
|
|
519
689
|
if (cycles.length > 0) {
|
|
@@ -577,5 +747,6 @@ var version = readVersion();
|
|
|
577
747
|
loadCharset,
|
|
578
748
|
loadConfig,
|
|
579
749
|
resolveCharsets,
|
|
750
|
+
resolvePaths,
|
|
580
751
|
version
|
|
581
752
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -42,6 +42,7 @@ declare function isDangerousChar(c: string): boolean;
|
|
|
42
42
|
* Returns a list of problem descriptions (empty if the file is clean).
|
|
43
43
|
*/
|
|
44
44
|
declare function checkFile(filePath: string, maxProblems?: number, extraRanges?: [number, number][], dangerousMap?: Map<number, string>): string[];
|
|
45
|
+
declare function resolvePaths(paths: string[]): string[];
|
|
45
46
|
/**
|
|
46
47
|
* Check multiple files. Returns 0 if all clean, 1 if any have problems.
|
|
47
48
|
*/
|
|
@@ -56,11 +57,11 @@ declare function checkPaths(paths: string[], extraRanges?: [number, number][], d
|
|
|
56
57
|
/** Convert a file path to a dotted module name (relative to srcDir). */
|
|
57
58
|
declare function fileToModule(filePath: string, srcDir: string): string;
|
|
58
59
|
/** Build a module-to-module dependency graph for the given package. */
|
|
59
|
-
declare function buildDependencyGraph(srcDir: string, basePackage: string): Map<string, Set<string
|
|
60
|
+
declare function buildDependencyGraph(srcDir: string, basePackage: string): Promise<Map<string, Set<string>>>;
|
|
60
61
|
/** Find all elementary cycles in the dependency graph using DFS. */
|
|
61
62
|
declare function findCycles(graph: Map<string, Set<string>>): string[][];
|
|
62
63
|
/** Run circular import detection. Returns 0 if clean, 1 if cycles found. */
|
|
63
|
-
declare function checkCircularImports(srcDir: string, basePackage: string): number
|
|
64
|
+
declare function checkCircularImports(srcDir: string, basePackage: string): Promise<number>;
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
67
|
* Configuration loading for apdev.
|
|
@@ -75,4 +76,4 @@ declare function loadConfig(projectDir?: string): Record<string, unknown>;
|
|
|
75
76
|
|
|
76
77
|
declare const version: string;
|
|
77
78
|
|
|
78
|
-
export { buildDependencyGraph, checkCircularImports, checkFile, checkPaths, fileToModule, findCycles, isAllowedChar, isDangerousChar, loadCharset, loadConfig, resolveCharsets, version };
|
|
79
|
+
export { buildDependencyGraph, checkCircularImports, checkFile, checkPaths, fileToModule, findCycles, isAllowedChar, isDangerousChar, loadCharset, loadConfig, resolveCharsets, resolvePaths, version };
|
package/dist/index.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ declare function isDangerousChar(c: string): boolean;
|
|
|
42
42
|
* Returns a list of problem descriptions (empty if the file is clean).
|
|
43
43
|
*/
|
|
44
44
|
declare function checkFile(filePath: string, maxProblems?: number, extraRanges?: [number, number][], dangerousMap?: Map<number, string>): string[];
|
|
45
|
+
declare function resolvePaths(paths: string[]): string[];
|
|
45
46
|
/**
|
|
46
47
|
* Check multiple files. Returns 0 if all clean, 1 if any have problems.
|
|
47
48
|
*/
|
|
@@ -56,11 +57,11 @@ declare function checkPaths(paths: string[], extraRanges?: [number, number][], d
|
|
|
56
57
|
/** Convert a file path to a dotted module name (relative to srcDir). */
|
|
57
58
|
declare function fileToModule(filePath: string, srcDir: string): string;
|
|
58
59
|
/** Build a module-to-module dependency graph for the given package. */
|
|
59
|
-
declare function buildDependencyGraph(srcDir: string, basePackage: string): Map<string, Set<string
|
|
60
|
+
declare function buildDependencyGraph(srcDir: string, basePackage: string): Promise<Map<string, Set<string>>>;
|
|
60
61
|
/** Find all elementary cycles in the dependency graph using DFS. */
|
|
61
62
|
declare function findCycles(graph: Map<string, Set<string>>): string[][];
|
|
62
63
|
/** Run circular import detection. Returns 0 if clean, 1 if cycles found. */
|
|
63
|
-
declare function checkCircularImports(srcDir: string, basePackage: string): number
|
|
64
|
+
declare function checkCircularImports(srcDir: string, basePackage: string): Promise<number>;
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
67
|
* Configuration loading for apdev.
|
|
@@ -75,4 +76,4 @@ declare function loadConfig(projectDir?: string): Record<string, unknown>;
|
|
|
75
76
|
|
|
76
77
|
declare const version: string;
|
|
77
78
|
|
|
78
|
-
export { buildDependencyGraph, checkCircularImports, checkFile, checkPaths, fileToModule, findCycles, isAllowedChar, isDangerousChar, loadCharset, loadConfig, resolveCharsets, version };
|
|
79
|
+
export { buildDependencyGraph, checkCircularImports, checkFile, checkPaths, fileToModule, findCycles, isAllowedChar, isDangerousChar, loadCharset, loadConfig, resolveCharsets, resolvePaths, version };
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ var getDirname = () => path.dirname(getFilename());
|
|
|
6
6
|
var __dirname = /* @__PURE__ */ getDirname();
|
|
7
7
|
|
|
8
8
|
// src/check-chars.ts
|
|
9
|
-
import { readFileSync, existsSync } from "fs";
|
|
9
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
10
10
|
import { extname, dirname, join, sep } from "path";
|
|
11
11
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12
12
|
function getCharsetsDir() {
|
|
@@ -260,10 +260,164 @@ function checkFile(filePath, maxProblems = 5, extraRanges, dangerousMap) {
|
|
|
260
260
|
}
|
|
261
261
|
return problems;
|
|
262
262
|
}
|
|
263
|
+
var SKIP_SUFFIXES = /* @__PURE__ */ new Set([
|
|
264
|
+
// Bytecode
|
|
265
|
+
".pyc",
|
|
266
|
+
".pyo",
|
|
267
|
+
// Images
|
|
268
|
+
".png",
|
|
269
|
+
".jpg",
|
|
270
|
+
".jpeg",
|
|
271
|
+
".gif",
|
|
272
|
+
".bmp",
|
|
273
|
+
".ico",
|
|
274
|
+
".svg",
|
|
275
|
+
".webp",
|
|
276
|
+
// Fonts
|
|
277
|
+
".ttf",
|
|
278
|
+
".otf",
|
|
279
|
+
".woff",
|
|
280
|
+
".woff2",
|
|
281
|
+
".eot",
|
|
282
|
+
// Archives
|
|
283
|
+
".zip",
|
|
284
|
+
".tar",
|
|
285
|
+
".gz",
|
|
286
|
+
".bz2",
|
|
287
|
+
".xz",
|
|
288
|
+
".7z",
|
|
289
|
+
// Compiled / binary
|
|
290
|
+
".so",
|
|
291
|
+
".dylib",
|
|
292
|
+
".dll",
|
|
293
|
+
".exe",
|
|
294
|
+
".o",
|
|
295
|
+
".a",
|
|
296
|
+
".whl",
|
|
297
|
+
".egg",
|
|
298
|
+
// Media
|
|
299
|
+
".mp3",
|
|
300
|
+
".mp4",
|
|
301
|
+
".wav",
|
|
302
|
+
".avi",
|
|
303
|
+
".mov",
|
|
304
|
+
".flac",
|
|
305
|
+
".ogg",
|
|
306
|
+
// Documents
|
|
307
|
+
".pdf",
|
|
308
|
+
".doc",
|
|
309
|
+
".docx",
|
|
310
|
+
".xls",
|
|
311
|
+
".xlsx",
|
|
312
|
+
".ppt",
|
|
313
|
+
".pptx",
|
|
314
|
+
// Data
|
|
315
|
+
".db",
|
|
316
|
+
".sqlite",
|
|
317
|
+
".sqlite3",
|
|
318
|
+
".pickle",
|
|
319
|
+
".pkl"
|
|
320
|
+
]);
|
|
321
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
322
|
+
"__pycache__",
|
|
323
|
+
"node_modules",
|
|
324
|
+
".git",
|
|
325
|
+
".venv",
|
|
326
|
+
"venv",
|
|
327
|
+
".tox",
|
|
328
|
+
".mypy_cache",
|
|
329
|
+
".pytest_cache",
|
|
330
|
+
".ruff_cache",
|
|
331
|
+
"dist",
|
|
332
|
+
"build"
|
|
333
|
+
]);
|
|
334
|
+
var DEFAULT_DIRS = ["src", "tests", "examples"];
|
|
335
|
+
var DEFAULT_GLOBS = ["*.md", "*.yml", "*.yaml", "*.json", ".gitignore"];
|
|
336
|
+
function walkDir(directory) {
|
|
337
|
+
const files = [];
|
|
338
|
+
let entries;
|
|
339
|
+
try {
|
|
340
|
+
entries = readdirSync(directory).sort();
|
|
341
|
+
} catch {
|
|
342
|
+
return files;
|
|
343
|
+
}
|
|
344
|
+
for (const name of entries) {
|
|
345
|
+
if (name.startsWith(".")) continue;
|
|
346
|
+
const fullPath = join(directory, name);
|
|
347
|
+
let stat;
|
|
348
|
+
try {
|
|
349
|
+
stat = statSync(fullPath);
|
|
350
|
+
} catch {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (stat.isDirectory()) {
|
|
354
|
+
if (SKIP_DIRS.has(name) || name.endsWith(".egg-info")) continue;
|
|
355
|
+
files.push(...walkDir(fullPath));
|
|
356
|
+
} else if (stat.isFile()) {
|
|
357
|
+
if (SKIP_SUFFIXES.has(extname(name).toLowerCase())) continue;
|
|
358
|
+
files.push(fullPath);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return files;
|
|
362
|
+
}
|
|
363
|
+
function defaultProjectFiles() {
|
|
364
|
+
const cwd = process.cwd();
|
|
365
|
+
const files = [];
|
|
366
|
+
for (const dirname3 of DEFAULT_DIRS) {
|
|
367
|
+
const d = join(cwd, dirname3);
|
|
368
|
+
if (existsSync(d) && statSync(d).isDirectory()) {
|
|
369
|
+
files.push(...walkDir(d));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
for (const pattern of DEFAULT_GLOBS) {
|
|
373
|
+
if (pattern.startsWith("*.")) {
|
|
374
|
+
const suffix = pattern.slice(1);
|
|
375
|
+
try {
|
|
376
|
+
for (const name of readdirSync(cwd).sort()) {
|
|
377
|
+
if (name.endsWith(suffix) && statSync(join(cwd, name)).isFile()) {
|
|
378
|
+
files.push(join(cwd, name));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
} catch {
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
const fullPath = join(cwd, pattern);
|
|
385
|
+
if (existsSync(fullPath) && statSync(fullPath).isFile()) {
|
|
386
|
+
files.push(fullPath);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return files;
|
|
391
|
+
}
|
|
392
|
+
function resolvePaths(paths) {
|
|
393
|
+
if (paths.length === 0) {
|
|
394
|
+
return defaultProjectFiles();
|
|
395
|
+
}
|
|
396
|
+
const result = [];
|
|
397
|
+
for (const p of paths) {
|
|
398
|
+
try {
|
|
399
|
+
if (statSync(p).isDirectory()) {
|
|
400
|
+
result.push(...walkDir(p));
|
|
401
|
+
} else {
|
|
402
|
+
result.push(p);
|
|
403
|
+
}
|
|
404
|
+
} catch {
|
|
405
|
+
result.push(p);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return result;
|
|
409
|
+
}
|
|
263
410
|
function checkPaths(paths, extraRanges, dangerousMap) {
|
|
411
|
+
const resolved = resolvePaths(paths);
|
|
412
|
+
if (resolved.length === 0) {
|
|
413
|
+
console.log("No files to check.");
|
|
414
|
+
return 0;
|
|
415
|
+
}
|
|
264
416
|
let hasError = false;
|
|
265
|
-
|
|
417
|
+
let checked = 0;
|
|
418
|
+
for (const path2 of resolved) {
|
|
266
419
|
const problems = checkFile(path2, 5, extraRanges, dangerousMap);
|
|
420
|
+
checked++;
|
|
267
421
|
if (problems.length > 0) {
|
|
268
422
|
hasError = true;
|
|
269
423
|
console.log(`
|
|
@@ -273,13 +427,27 @@ ${path2} contains illegal characters:`);
|
|
|
273
427
|
}
|
|
274
428
|
}
|
|
275
429
|
}
|
|
430
|
+
if (!hasError) {
|
|
431
|
+
console.log(`All ${checked} files passed.`);
|
|
432
|
+
}
|
|
276
433
|
return hasError ? 1 : 0;
|
|
277
434
|
}
|
|
278
435
|
|
|
279
436
|
// src/check-imports.ts
|
|
280
|
-
import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
437
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
281
438
|
import { join as join2, relative, sep as sep2, extname as extname2, basename } from "path";
|
|
282
|
-
|
|
439
|
+
var _ts;
|
|
440
|
+
async function loadTS() {
|
|
441
|
+
if (_ts) return _ts;
|
|
442
|
+
try {
|
|
443
|
+
_ts = (await import("typescript")).default;
|
|
444
|
+
return _ts;
|
|
445
|
+
} catch {
|
|
446
|
+
throw new Error(
|
|
447
|
+
"The 'typescript' package is required for circular import detection.\nInstall it with: npm install -g typescript\nOr locally: npm install -D typescript"
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
283
451
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
284
452
|
".ts",
|
|
285
453
|
".tsx",
|
|
@@ -308,7 +476,8 @@ function fileToModule(filePath, srcDir) {
|
|
|
308
476
|
}
|
|
309
477
|
return parts.join(".");
|
|
310
478
|
}
|
|
311
|
-
function extractImports(source, fileName) {
|
|
479
|
+
async function extractImports(source, fileName) {
|
|
480
|
+
const ts = await loadTS();
|
|
312
481
|
const imports = /* @__PURE__ */ new Set();
|
|
313
482
|
const sourceFile = ts.createSourceFile(
|
|
314
483
|
fileName,
|
|
@@ -366,13 +535,13 @@ function resolveImports(rawImports, basePackage, currentModule, isPackage) {
|
|
|
366
535
|
function findSourceFiles(dir) {
|
|
367
536
|
const results = [];
|
|
368
537
|
function walk(d) {
|
|
369
|
-
const entries =
|
|
538
|
+
const entries = readdirSync2(d);
|
|
370
539
|
for (const entry of entries) {
|
|
371
540
|
if (entry === "node_modules" || entry === "dist" || entry === ".git") {
|
|
372
541
|
continue;
|
|
373
542
|
}
|
|
374
543
|
const full = join2(d, entry);
|
|
375
|
-
const stat =
|
|
544
|
+
const stat = statSync2(full);
|
|
376
545
|
if (stat.isDirectory()) {
|
|
377
546
|
walk(full);
|
|
378
547
|
} else if (SUPPORTED_EXTENSIONS.has(extname2(entry))) {
|
|
@@ -383,7 +552,7 @@ function findSourceFiles(dir) {
|
|
|
383
552
|
walk(dir);
|
|
384
553
|
return results;
|
|
385
554
|
}
|
|
386
|
-
function buildDependencyGraph(srcDir, basePackage) {
|
|
555
|
+
async function buildDependencyGraph(srcDir, basePackage) {
|
|
387
556
|
const graph = /* @__PURE__ */ new Map();
|
|
388
557
|
const files = findSourceFiles(srcDir);
|
|
389
558
|
for (const file of files) {
|
|
@@ -398,7 +567,7 @@ function buildDependencyGraph(srcDir, basePackage) {
|
|
|
398
567
|
}
|
|
399
568
|
let rawImports;
|
|
400
569
|
try {
|
|
401
|
-
rawImports = extractImports(source, file);
|
|
570
|
+
rawImports = await extractImports(source, file);
|
|
402
571
|
} catch (e) {
|
|
403
572
|
console.error(`Warning: could not parse ${file}: ${e}`);
|
|
404
573
|
continue;
|
|
@@ -457,10 +626,10 @@ function findCycles(graph) {
|
|
|
457
626
|
}
|
|
458
627
|
return unique;
|
|
459
628
|
}
|
|
460
|
-
function checkCircularImports(srcDir, basePackage) {
|
|
629
|
+
async function checkCircularImports(srcDir, basePackage) {
|
|
461
630
|
let stat;
|
|
462
631
|
try {
|
|
463
|
-
stat =
|
|
632
|
+
stat = statSync2(srcDir);
|
|
464
633
|
} catch {
|
|
465
634
|
console.error(`Error: ${srcDir}/ directory not found`);
|
|
466
635
|
return 1;
|
|
@@ -469,7 +638,7 @@ function checkCircularImports(srcDir, basePackage) {
|
|
|
469
638
|
console.error(`Error: ${srcDir}/ is not a directory`);
|
|
470
639
|
return 1;
|
|
471
640
|
}
|
|
472
|
-
const graph = buildDependencyGraph(srcDir, basePackage);
|
|
641
|
+
const graph = await buildDependencyGraph(srcDir, basePackage);
|
|
473
642
|
console.log(`Scanned ${graph.size} modules`);
|
|
474
643
|
const cycles = findCycles(graph);
|
|
475
644
|
if (cycles.length > 0) {
|
|
@@ -532,5 +701,6 @@ export {
|
|
|
532
701
|
loadCharset,
|
|
533
702
|
loadConfig,
|
|
534
703
|
resolveCharsets,
|
|
704
|
+
resolvePaths,
|
|
535
705
|
version
|
|
536
706
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apdev-js",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "General-purpose development tools for TypeScript/JavaScript projects - character validation, circular import detection, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -31,9 +31,16 @@
|
|
|
31
31
|
},
|
|
32
32
|
"keywords": [
|
|
33
33
|
"development-tools",
|
|
34
|
+
"quality-assurance",
|
|
35
|
+
"linting",
|
|
36
|
+
"static-analysis",
|
|
34
37
|
"character-validation",
|
|
35
38
|
"circular-imports",
|
|
36
|
-
"
|
|
39
|
+
"release-automation",
|
|
40
|
+
"unicode-checker",
|
|
41
|
+
"trojan-source-detection",
|
|
42
|
+
"typescript",
|
|
43
|
+
"javascript"
|
|
37
44
|
],
|
|
38
45
|
"author": "aipartnerup <tercel.yi@gmail.com>",
|
|
39
46
|
"license": "Apache-2.0",
|
|
@@ -63,4 +70,4 @@
|
|
|
63
70
|
"typescript": "^5.7.0",
|
|
64
71
|
"vitest": "^3.0.0"
|
|
65
72
|
}
|
|
66
|
-
}
|
|
73
|
+
}
|