apdev-js 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.js +290 -98
- package/dist/index.cjs +269 -79
- package/dist/index.d.cts +42 -4
- package/dist/index.d.ts +42 -4
- package/dist/index.js +278 -92
- package/package.json +23 -7
- package/src/charsets/base.json +44 -0
- package/src/charsets/chinese.json +9 -0
- package/src/charsets/japanese.json +11 -0
- package/src/charsets/korean.json +11 -0
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
|
@@ -9,88 +9,98 @@ var __dirname = /* @__PURE__ */ getDirname();
|
|
|
9
9
|
|
|
10
10
|
// src/cli.ts
|
|
11
11
|
import { readFileSync as readFileSync4 } from "fs";
|
|
12
|
-
import { resolve, dirname, join as
|
|
13
|
-
import { fileURLToPath as
|
|
12
|
+
import { resolve, dirname as dirname2, join as join4, sep as sep3 } from "path";
|
|
13
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14
14
|
import { execFileSync } from "child_process";
|
|
15
15
|
import { Command } from "commander";
|
|
16
16
|
|
|
17
17
|
// src/check-chars.ts
|
|
18
|
-
import { readFileSync } from "fs";
|
|
19
|
-
import { extname } from "path";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
[
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
[
|
|
56
|
-
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// Bidi control characters (Trojan Source - CVE-2021-42574)
|
|
61
|
-
[8234, "LEFT-TO-RIGHT EMBEDDING"],
|
|
62
|
-
[8235, "RIGHT-TO-LEFT EMBEDDING"],
|
|
63
|
-
[8236, "POP DIRECTIONAL FORMATTING"],
|
|
64
|
-
[8237, "LEFT-TO-RIGHT OVERRIDE"],
|
|
65
|
-
[8238, "RIGHT-TO-LEFT OVERRIDE"],
|
|
66
|
-
[8294, "LEFT-TO-RIGHT ISOLATE"],
|
|
67
|
-
[8295, "RIGHT-TO-LEFT ISOLATE"],
|
|
68
|
-
[8296, "FIRST STRONG ISOLATE"],
|
|
69
|
-
[8297, "POP DIRECTIONAL ISOLATE"],
|
|
70
|
-
// Zero-width characters
|
|
71
|
-
[8203, "ZERO WIDTH SPACE"],
|
|
72
|
-
[8204, "ZERO WIDTH NON-JOINER"],
|
|
73
|
-
[8205, "ZERO WIDTH JOINER"],
|
|
74
|
-
[8206, "LEFT-TO-RIGHT MARK"],
|
|
75
|
-
[8207, "RIGHT-TO-LEFT MARK"],
|
|
76
|
-
[8288, "WORD JOINER"]
|
|
77
|
-
]);
|
|
78
|
-
var PYTHON_SUFFIXES = /* @__PURE__ */ new Set([".py"]);
|
|
79
|
-
var JS_SUFFIXES = /* @__PURE__ */ new Set([".js", ".ts", ".tsx", ".jsx", ".mjs", ".cjs"]);
|
|
80
|
-
function isAllowedChar(c) {
|
|
81
|
-
const code = c.codePointAt(0);
|
|
82
|
-
if (code <= 127) {
|
|
83
|
-
return true;
|
|
18
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
19
|
+
import { extname, dirname, join, sep } from "path";
|
|
20
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
21
|
+
function getCharsetsDir() {
|
|
22
|
+
const thisDir = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath2(import.meta.url));
|
|
23
|
+
const devPath = join(thisDir, "charsets");
|
|
24
|
+
if (existsSync(devPath)) {
|
|
25
|
+
return devPath;
|
|
26
|
+
}
|
|
27
|
+
return join(thisDir, "..", "src", "charsets");
|
|
28
|
+
}
|
|
29
|
+
function loadCharset(nameOrPath) {
|
|
30
|
+
if (nameOrPath.includes(sep) || nameOrPath.includes("/") || nameOrPath.endsWith(".json")) {
|
|
31
|
+
if (!existsSync(nameOrPath)) {
|
|
32
|
+
throw new Error(`Charset file not found: ${nameOrPath}`);
|
|
33
|
+
}
|
|
34
|
+
return JSON.parse(readFileSync(nameOrPath, "utf-8"));
|
|
35
|
+
}
|
|
36
|
+
const filePath = join(getCharsetsDir(), `${nameOrPath}.json`);
|
|
37
|
+
if (!existsSync(filePath)) {
|
|
38
|
+
throw new Error(`Unknown charset: ${nameOrPath}`);
|
|
39
|
+
}
|
|
40
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
41
|
+
}
|
|
42
|
+
function parseRanges(entries) {
|
|
43
|
+
return entries.map((e) => [parseInt(e.start, 16), parseInt(e.end, 16)]);
|
|
44
|
+
}
|
|
45
|
+
function parseDangerous(entries) {
|
|
46
|
+
const map = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const e of entries) {
|
|
48
|
+
map.set(parseInt(e.code, 16), e.name);
|
|
49
|
+
}
|
|
50
|
+
return map;
|
|
51
|
+
}
|
|
52
|
+
function resolveCharsets(charsetNames, charsetFiles) {
|
|
53
|
+
const base = loadCharset("base");
|
|
54
|
+
const rangesSet = /* @__PURE__ */ new Map();
|
|
55
|
+
const dangerous = parseDangerous(base.dangerous ?? []);
|
|
56
|
+
function addRanges(entries) {
|
|
57
|
+
for (const [s, e] of parseRanges(entries)) {
|
|
58
|
+
rangesSet.set(`${s}-${e}`, [s, e]);
|
|
59
|
+
}
|
|
84
60
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
61
|
+
addRanges(base.emoji_ranges ?? []);
|
|
62
|
+
addRanges(base.extra_ranges ?? []);
|
|
63
|
+
for (const name of charsetNames) {
|
|
64
|
+
const data = loadCharset(name);
|
|
65
|
+
addRanges(data.emoji_ranges ?? []);
|
|
66
|
+
addRanges(data.extra_ranges ?? []);
|
|
67
|
+
if (data.dangerous) {
|
|
68
|
+
for (const [code, dname] of parseDangerous(data.dangerous)) {
|
|
69
|
+
dangerous.set(code, dname);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const path2 of charsetFiles) {
|
|
74
|
+
const data = loadCharset(path2);
|
|
75
|
+
addRanges(data.emoji_ranges ?? []);
|
|
76
|
+
addRanges(data.extra_ranges ?? []);
|
|
77
|
+
if (data.dangerous) {
|
|
78
|
+
for (const [code, dname] of parseDangerous(data.dangerous)) {
|
|
79
|
+
dangerous.set(code, dname);
|
|
80
|
+
}
|
|
88
81
|
}
|
|
89
82
|
}
|
|
83
|
+
const ranges = [...rangesSet.values()].sort((a, b) => a[0] - b[0]);
|
|
84
|
+
return { ranges, dangerous };
|
|
85
|
+
}
|
|
86
|
+
var PYTHON_SUFFIXES = /* @__PURE__ */ new Set([".py"]);
|
|
87
|
+
var JS_SUFFIXES = /* @__PURE__ */ new Set([".js", ".ts", ".tsx", ".jsx", ".mjs", ".cjs"]);
|
|
88
|
+
function isInRanges(code, ranges) {
|
|
89
|
+
if (code <= 127) return true;
|
|
90
|
+
for (const [start, end] of ranges) {
|
|
91
|
+
if (code >= start && code <= end) return true;
|
|
92
|
+
}
|
|
90
93
|
return false;
|
|
91
94
|
}
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
var _baseRanges = null;
|
|
96
|
+
var _baseDangerous = null;
|
|
97
|
+
function getBaseDefaults() {
|
|
98
|
+
if (!_baseRanges || !_baseDangerous) {
|
|
99
|
+
const defaults = resolveCharsets([], []);
|
|
100
|
+
_baseRanges = defaults.ranges;
|
|
101
|
+
_baseDangerous = defaults.dangerous;
|
|
102
|
+
}
|
|
103
|
+
return { ranges: _baseRanges, dangerous: _baseDangerous };
|
|
94
104
|
}
|
|
95
105
|
function computeCommentMask(content, suffix) {
|
|
96
106
|
if (PYTHON_SUFFIXES.has(suffix)) {
|
|
@@ -209,8 +219,13 @@ function computeCommentMaskJs(content) {
|
|
|
209
219
|
}
|
|
210
220
|
return mask;
|
|
211
221
|
}
|
|
212
|
-
function checkFile(filePath, maxProblems = 5) {
|
|
222
|
+
function checkFile(filePath, maxProblems = 5, extraRanges, dangerousMap) {
|
|
213
223
|
const problems = [];
|
|
224
|
+
if (!extraRanges || !dangerousMap) {
|
|
225
|
+
const defaults = getBaseDefaults();
|
|
226
|
+
extraRanges ??= defaults.ranges;
|
|
227
|
+
dangerousMap ??= defaults.dangerous;
|
|
228
|
+
}
|
|
214
229
|
try {
|
|
215
230
|
const content = readFileSync(filePath, "utf-8");
|
|
216
231
|
const suffix = extname(filePath).toLowerCase();
|
|
@@ -220,15 +235,15 @@ function checkFile(filePath, maxProblems = 5) {
|
|
|
220
235
|
for (const char of content) {
|
|
221
236
|
position++;
|
|
222
237
|
const code = char.codePointAt(0);
|
|
223
|
-
if (
|
|
238
|
+
if (dangerousMap.has(code)) {
|
|
224
239
|
if (!commentMask.has(offset)) {
|
|
225
|
-
const name =
|
|
240
|
+
const name = dangerousMap.get(code);
|
|
226
241
|
const hex = code.toString(16).toUpperCase().padStart(4, "0");
|
|
227
242
|
problems.push(
|
|
228
243
|
`Dangerous character in code at position ${position}: U+${hex} (${name})`
|
|
229
244
|
);
|
|
230
245
|
}
|
|
231
|
-
} else if (!
|
|
246
|
+
} else if (!isInRanges(code, extraRanges)) {
|
|
232
247
|
const hex = code.toString(16).toUpperCase().padStart(4, "0");
|
|
233
248
|
problems.push(
|
|
234
249
|
`Illegal character at position ${position}: ${JSON.stringify(char)} (U+${hex})`
|
|
@@ -239,15 +254,169 @@ function checkFile(filePath, maxProblems = 5) {
|
|
|
239
254
|
}
|
|
240
255
|
offset += char.length;
|
|
241
256
|
}
|
|
242
|
-
} catch {
|
|
243
|
-
problems.push(`Failed to read file: ${filePath}`);
|
|
257
|
+
} catch (e) {
|
|
258
|
+
problems.push(`Failed to read file: ${filePath} (${e})`);
|
|
244
259
|
}
|
|
245
260
|
return problems;
|
|
246
261
|
}
|
|
247
|
-
|
|
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
|
+
}
|
|
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
|
+
}
|
|
248
415
|
let hasError = false;
|
|
249
|
-
|
|
250
|
-
|
|
416
|
+
let checked = 0;
|
|
417
|
+
for (const path2 of resolved) {
|
|
418
|
+
const problems = checkFile(path2, 5, extraRanges, dangerousMap);
|
|
419
|
+
checked++;
|
|
251
420
|
if (problems.length > 0) {
|
|
252
421
|
hasError = true;
|
|
253
422
|
console.log(`
|
|
@@ -257,12 +426,15 @@ ${path2} contains illegal characters:`);
|
|
|
257
426
|
}
|
|
258
427
|
}
|
|
259
428
|
}
|
|
429
|
+
if (!hasError) {
|
|
430
|
+
console.log(`All ${checked} files passed.`);
|
|
431
|
+
}
|
|
260
432
|
return hasError ? 1 : 0;
|
|
261
433
|
}
|
|
262
434
|
|
|
263
435
|
// src/check-imports.ts
|
|
264
|
-
import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
|
|
265
|
-
import { join, relative, sep, extname as extname2, basename } from "path";
|
|
436
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
437
|
+
import { join as join2, relative, sep as sep2, extname as extname2, basename } from "path";
|
|
266
438
|
import ts from "typescript";
|
|
267
439
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
268
440
|
".ts",
|
|
@@ -282,7 +454,7 @@ var INDEX_BASENAMES = /* @__PURE__ */ new Set([
|
|
|
282
454
|
]);
|
|
283
455
|
function fileToModule(filePath, srcDir) {
|
|
284
456
|
const rel = relative(srcDir, filePath);
|
|
285
|
-
const parts = rel.split(
|
|
457
|
+
const parts = rel.split(sep2);
|
|
286
458
|
const last = parts[parts.length - 1];
|
|
287
459
|
if (last === "index.ts" || last === "index.js" || last === "index.tsx" || last === "index.jsx") {
|
|
288
460
|
parts.pop();
|
|
@@ -350,13 +522,13 @@ function resolveImports(rawImports, basePackage, currentModule, isPackage) {
|
|
|
350
522
|
function findSourceFiles(dir) {
|
|
351
523
|
const results = [];
|
|
352
524
|
function walk(d) {
|
|
353
|
-
const entries =
|
|
525
|
+
const entries = readdirSync2(d);
|
|
354
526
|
for (const entry of entries) {
|
|
355
527
|
if (entry === "node_modules" || entry === "dist" || entry === ".git") {
|
|
356
528
|
continue;
|
|
357
529
|
}
|
|
358
|
-
const full =
|
|
359
|
-
const stat =
|
|
530
|
+
const full = join2(d, entry);
|
|
531
|
+
const stat = statSync2(full);
|
|
360
532
|
if (stat.isDirectory()) {
|
|
361
533
|
walk(full);
|
|
362
534
|
} else if (SUPPORTED_EXTENSIONS.has(extname2(entry))) {
|
|
@@ -444,7 +616,7 @@ function findCycles(graph) {
|
|
|
444
616
|
function checkCircularImports(srcDir, basePackage) {
|
|
445
617
|
let stat;
|
|
446
618
|
try {
|
|
447
|
-
stat =
|
|
619
|
+
stat = statSync2(srcDir);
|
|
448
620
|
} catch {
|
|
449
621
|
console.error(`Error: ${srcDir}/ directory not found`);
|
|
450
622
|
return 1;
|
|
@@ -472,10 +644,10 @@ Found ${cycles.length} circular import(s):
|
|
|
472
644
|
|
|
473
645
|
// src/config.ts
|
|
474
646
|
import { readFileSync as readFileSync3 } from "fs";
|
|
475
|
-
import { join as
|
|
647
|
+
import { join as join3 } from "path";
|
|
476
648
|
function loadConfig(projectDir) {
|
|
477
649
|
const dir = projectDir ?? process.cwd();
|
|
478
|
-
const pkgPath =
|
|
650
|
+
const pkgPath = join3(dir, "package.json");
|
|
479
651
|
let raw;
|
|
480
652
|
try {
|
|
481
653
|
raw = readFileSync3(pkgPath, "utf-8");
|
|
@@ -491,9 +663,12 @@ function loadConfig(projectDir) {
|
|
|
491
663
|
}
|
|
492
664
|
|
|
493
665
|
// src/cli.ts
|
|
666
|
+
function collect(value, previous) {
|
|
667
|
+
return [...previous, value];
|
|
668
|
+
}
|
|
494
669
|
function getVersion() {
|
|
495
|
-
const thisDir = typeof __dirname !== "undefined" ? __dirname :
|
|
496
|
-
const pkgPath =
|
|
670
|
+
const thisDir = typeof __dirname !== "undefined" ? __dirname : dirname2(fileURLToPath3(import.meta.url));
|
|
671
|
+
const pkgPath = join4(thisDir, "..", "package.json");
|
|
497
672
|
try {
|
|
498
673
|
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
499
674
|
return pkg.version ?? "0.0.0";
|
|
@@ -502,15 +677,32 @@ function getVersion() {
|
|
|
502
677
|
}
|
|
503
678
|
}
|
|
504
679
|
function getReleaseScript() {
|
|
505
|
-
const thisDir = typeof __dirname !== "undefined" ? __dirname :
|
|
506
|
-
return
|
|
680
|
+
const thisDir = typeof __dirname !== "undefined" ? __dirname : dirname2(fileURLToPath3(import.meta.url));
|
|
681
|
+
return join4(thisDir, "..", "release.sh");
|
|
507
682
|
}
|
|
508
683
|
function buildProgram() {
|
|
509
684
|
const program2 = new Command();
|
|
510
685
|
program2.name("apdev").description("Shared development tools for TypeScript/JavaScript projects").version(getVersion());
|
|
511
|
-
program2.command("check-chars").description("Validate files contain only allowed characters").argument("
|
|
686
|
+
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) => {
|
|
687
|
+
let charsetNames = opts.charset;
|
|
688
|
+
let charsetFiles = opts.charsetFile;
|
|
689
|
+
if (charsetNames.length === 0 && charsetFiles.length === 0) {
|
|
690
|
+
const envVal = process.env.APDEV_EXTRA_CHARS ?? "";
|
|
691
|
+
if (envVal) {
|
|
692
|
+
for (const item of envVal.split(",")) {
|
|
693
|
+
const trimmed = item.trim();
|
|
694
|
+
if (!trimmed) continue;
|
|
695
|
+
if (trimmed.includes("/") || trimmed.includes(sep3) || trimmed.endsWith(".json")) {
|
|
696
|
+
charsetFiles.push(trimmed);
|
|
697
|
+
} else {
|
|
698
|
+
charsetNames.push(trimmed);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const { ranges, dangerous } = resolveCharsets(charsetNames, charsetFiles);
|
|
512
704
|
const resolved = files.map((f) => resolve(f));
|
|
513
|
-
const code = checkPaths(resolved);
|
|
705
|
+
const code = checkPaths(resolved, ranges, dangerous);
|
|
514
706
|
process.exit(code);
|
|
515
707
|
});
|
|
516
708
|
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) => {
|
|
@@ -526,7 +718,7 @@ function buildProgram() {
|
|
|
526
718
|
const code = checkCircularImports(resolve(srcDir), basePackage);
|
|
527
719
|
process.exit(code);
|
|
528
720
|
});
|
|
529
|
-
program2.command("release").description("Interactive release automation (build, tag, GitHub release, npm publish)").option("--yes
|
|
721
|
+
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) => {
|
|
530
722
|
const script = getReleaseScript();
|
|
531
723
|
try {
|
|
532
724
|
readFileSync4(script);
|