apdev-js 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,508 @@
1
+ // node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ var getFilename = () => fileURLToPath(import.meta.url);
5
+ var getDirname = () => path.dirname(getFilename());
6
+ var __dirname = /* @__PURE__ */ getDirname();
7
+
8
+ // src/check-chars.ts
9
+ import { readFileSync } from "fs";
10
+ import { extname } from "path";
11
+ var EMOJI_RANGES = [
12
+ [127744, 128511],
13
+ // Symbols and Pictographs
14
+ [128512, 128591],
15
+ // Emoticons
16
+ [128640, 128767],
17
+ // Transport and Map Symbols
18
+ [128896, 129023],
19
+ // Geometric Shapes Extended
20
+ [129280, 129535],
21
+ // Supplemental Symbols and Pictographs
22
+ [9728, 9983],
23
+ // Miscellaneous Symbols
24
+ [9984, 10175]
25
+ // Dingbats
26
+ ];
27
+ var EXTRA_ALLOWED_RANGES = [
28
+ [128, 255],
29
+ // Latin-1 Supplement
30
+ [8192, 8303],
31
+ // General Punctuation
32
+ [8448, 8527],
33
+ // Letterlike Symbols
34
+ [8592, 8703],
35
+ // Arrows
36
+ [8704, 8959],
37
+ // Mathematical Operators
38
+ [8960, 9215],
39
+ // Miscellaneous Technical
40
+ [9472, 9599],
41
+ // Box Drawing
42
+ [9632, 9727],
43
+ // Geometric Shapes
44
+ [11008, 11263],
45
+ // Miscellaneous Symbols and Arrows
46
+ [65024, 65039]
47
+ // Variation Selectors
48
+ ];
49
+ var ALL_RANGES = [...EMOJI_RANGES, ...EXTRA_ALLOWED_RANGES];
50
+ var DANGEROUS_CODEPOINTS = /* @__PURE__ */ new Map([
51
+ // Bidi control characters (Trojan Source - CVE-2021-42574)
52
+ [8234, "LEFT-TO-RIGHT EMBEDDING"],
53
+ [8235, "RIGHT-TO-LEFT EMBEDDING"],
54
+ [8236, "POP DIRECTIONAL FORMATTING"],
55
+ [8237, "LEFT-TO-RIGHT OVERRIDE"],
56
+ [8238, "RIGHT-TO-LEFT OVERRIDE"],
57
+ [8294, "LEFT-TO-RIGHT ISOLATE"],
58
+ [8295, "RIGHT-TO-LEFT ISOLATE"],
59
+ [8296, "FIRST STRONG ISOLATE"],
60
+ [8297, "POP DIRECTIONAL ISOLATE"],
61
+ // Zero-width characters
62
+ [8203, "ZERO WIDTH SPACE"],
63
+ [8204, "ZERO WIDTH NON-JOINER"],
64
+ [8205, "ZERO WIDTH JOINER"],
65
+ [8206, "LEFT-TO-RIGHT MARK"],
66
+ [8207, "RIGHT-TO-LEFT MARK"],
67
+ [8288, "WORD JOINER"]
68
+ ]);
69
+ var PYTHON_SUFFIXES = /* @__PURE__ */ new Set([".py"]);
70
+ var JS_SUFFIXES = /* @__PURE__ */ new Set([".js", ".ts", ".tsx", ".jsx", ".mjs", ".cjs"]);
71
+ function isAllowedChar(c) {
72
+ const code = c.codePointAt(0);
73
+ if (code <= 127) {
74
+ return true;
75
+ }
76
+ for (const [start, end] of ALL_RANGES) {
77
+ if (code >= start && code <= end) {
78
+ return true;
79
+ }
80
+ }
81
+ return false;
82
+ }
83
+ function isDangerousChar(c) {
84
+ return DANGEROUS_CODEPOINTS.has(c.codePointAt(0));
85
+ }
86
+ function computeCommentMask(content, suffix) {
87
+ if (PYTHON_SUFFIXES.has(suffix)) {
88
+ return computeCommentMaskPython(content);
89
+ }
90
+ if (JS_SUFFIXES.has(suffix)) {
91
+ return computeCommentMaskJs(content);
92
+ }
93
+ return /* @__PURE__ */ new Set();
94
+ }
95
+ function computeCommentMaskPython(content) {
96
+ const mask = /* @__PURE__ */ new Set();
97
+ let i = 0;
98
+ const n = content.length;
99
+ while (i < n) {
100
+ if (i + 2 < n && (content.slice(i, i + 3) === '"""' || content.slice(i, i + 3) === "'''")) {
101
+ const quote = content.slice(i, i + 3);
102
+ i += 3;
103
+ while (i < n) {
104
+ if (content[i] === "\\" && i + 1 < n) {
105
+ i += 2;
106
+ continue;
107
+ }
108
+ if (i + 2 < n && content.slice(i, i + 3) === quote) {
109
+ i += 3;
110
+ break;
111
+ }
112
+ i++;
113
+ }
114
+ continue;
115
+ }
116
+ if (content[i] === '"' || content[i] === "'") {
117
+ const quoteChar = content[i];
118
+ i++;
119
+ while (i < n && content[i] !== "\n") {
120
+ if (content[i] === "\\" && i + 1 < n) {
121
+ i += 2;
122
+ continue;
123
+ }
124
+ if (content[i] === quoteChar) {
125
+ i++;
126
+ break;
127
+ }
128
+ i++;
129
+ }
130
+ continue;
131
+ }
132
+ if (content[i] === "#") {
133
+ while (i < n && content[i] !== "\n") {
134
+ mask.add(i);
135
+ i++;
136
+ }
137
+ continue;
138
+ }
139
+ i++;
140
+ }
141
+ return mask;
142
+ }
143
+ function computeCommentMaskJs(content) {
144
+ const mask = /* @__PURE__ */ new Set();
145
+ let i = 0;
146
+ const n = content.length;
147
+ while (i < n) {
148
+ if (content[i] === "`") {
149
+ i++;
150
+ while (i < n) {
151
+ if (content[i] === "\\" && i + 1 < n) {
152
+ i += 2;
153
+ continue;
154
+ }
155
+ if (content[i] === "`") {
156
+ i++;
157
+ break;
158
+ }
159
+ i++;
160
+ }
161
+ continue;
162
+ }
163
+ if (content[i] === '"' || content[i] === "'") {
164
+ const quoteChar = content[i];
165
+ i++;
166
+ while (i < n && content[i] !== "\n") {
167
+ if (content[i] === "\\" && i + 1 < n) {
168
+ i += 2;
169
+ continue;
170
+ }
171
+ if (content[i] === quoteChar) {
172
+ i++;
173
+ break;
174
+ }
175
+ i++;
176
+ }
177
+ continue;
178
+ }
179
+ if (i + 1 < n && content[i] === "/" && content[i + 1] === "/") {
180
+ while (i < n && content[i] !== "\n") {
181
+ mask.add(i);
182
+ i++;
183
+ }
184
+ continue;
185
+ }
186
+ if (i + 1 < n && content[i] === "/" && content[i + 1] === "*") {
187
+ while (i < n) {
188
+ if (i + 1 < n && content[i] === "*" && content[i + 1] === "/") {
189
+ mask.add(i);
190
+ mask.add(i + 1);
191
+ i += 2;
192
+ break;
193
+ }
194
+ mask.add(i);
195
+ i++;
196
+ }
197
+ continue;
198
+ }
199
+ i++;
200
+ }
201
+ return mask;
202
+ }
203
+ function checkFile(filePath, maxProblems = 5) {
204
+ const problems = [];
205
+ try {
206
+ const content = readFileSync(filePath, "utf-8");
207
+ const suffix = extname(filePath).toLowerCase();
208
+ const commentMask = computeCommentMask(content, suffix);
209
+ let position = 0;
210
+ let offset = 0;
211
+ for (const char of content) {
212
+ position++;
213
+ const code = char.codePointAt(0);
214
+ if (isDangerousChar(char)) {
215
+ if (!commentMask.has(offset)) {
216
+ const name = DANGEROUS_CODEPOINTS.get(code);
217
+ const hex = code.toString(16).toUpperCase().padStart(4, "0");
218
+ problems.push(
219
+ `Dangerous character in code at position ${position}: U+${hex} (${name})`
220
+ );
221
+ }
222
+ } else if (!isAllowedChar(char)) {
223
+ const hex = code.toString(16).toUpperCase().padStart(4, "0");
224
+ problems.push(
225
+ `Illegal character at position ${position}: ${JSON.stringify(char)} (U+${hex})`
226
+ );
227
+ }
228
+ if (problems.length >= maxProblems) {
229
+ break;
230
+ }
231
+ offset += char.length;
232
+ }
233
+ } catch {
234
+ problems.push(`Failed to read file: ${filePath}`);
235
+ }
236
+ return problems;
237
+ }
238
+ function checkPaths(paths) {
239
+ let hasError = false;
240
+ for (const path2 of paths) {
241
+ const problems = checkFile(path2);
242
+ if (problems.length > 0) {
243
+ hasError = true;
244
+ console.log(`
245
+ ${path2} contains illegal characters:`);
246
+ for (const p of problems) {
247
+ console.log(` ${p}`);
248
+ }
249
+ }
250
+ }
251
+ return hasError ? 1 : 0;
252
+ }
253
+
254
+ // src/check-imports.ts
255
+ import { readFileSync as readFileSync2, readdirSync, statSync } from "fs";
256
+ import { join, relative, sep, extname as extname2, basename } from "path";
257
+ import ts from "typescript";
258
+ var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([
259
+ ".ts",
260
+ ".tsx",
261
+ ".js",
262
+ ".jsx",
263
+ ".mjs",
264
+ ".cjs"
265
+ ]);
266
+ var INDEX_BASENAMES = /* @__PURE__ */ new Set([
267
+ "index.ts",
268
+ "index.tsx",
269
+ "index.js",
270
+ "index.jsx",
271
+ "index.mjs",
272
+ "index.cjs"
273
+ ]);
274
+ function fileToModule(filePath, srcDir) {
275
+ const rel = relative(srcDir, filePath);
276
+ const parts = rel.split(sep);
277
+ const last = parts[parts.length - 1];
278
+ if (last === "index.ts" || last === "index.js" || last === "index.tsx" || last === "index.jsx") {
279
+ parts.pop();
280
+ } else {
281
+ const ext = extname2(last);
282
+ parts[parts.length - 1] = basename(last, ext);
283
+ }
284
+ return parts.join(".");
285
+ }
286
+ function extractImports(source, fileName) {
287
+ const imports = /* @__PURE__ */ new Set();
288
+ const sourceFile = ts.createSourceFile(
289
+ fileName,
290
+ source,
291
+ ts.ScriptTarget.Latest,
292
+ true,
293
+ fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : void 0
294
+ );
295
+ function visit(node) {
296
+ if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
297
+ imports.add(node.moduleSpecifier.text);
298
+ }
299
+ if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
300
+ imports.add(node.moduleSpecifier.text);
301
+ }
302
+ if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.Identifier && node.expression.text === "require" && node.arguments.length === 1 && ts.isStringLiteral(node.arguments[0])) {
303
+ imports.add(node.arguments[0].text);
304
+ }
305
+ ts.forEachChild(node, visit);
306
+ }
307
+ visit(sourceFile);
308
+ return imports;
309
+ }
310
+ function resolveImports(rawImports, basePackage, currentModule, isPackage) {
311
+ const resolved = /* @__PURE__ */ new Set();
312
+ for (const imp of rawImports) {
313
+ if (imp.startsWith("./") || imp.startsWith("../")) {
314
+ if (!currentModule) continue;
315
+ const moduleParts = currentModule.split(".");
316
+ let dirParts;
317
+ if (isPackage) {
318
+ dirParts = [...moduleParts];
319
+ } else {
320
+ dirParts = moduleParts.slice(0, -1);
321
+ }
322
+ const segments = imp.split("/");
323
+ for (const seg of segments) {
324
+ if (seg === ".") continue;
325
+ if (seg === "..") {
326
+ dirParts.pop();
327
+ } else {
328
+ dirParts.push(seg);
329
+ }
330
+ }
331
+ const resolvedModule = dirParts.join(".");
332
+ if (resolvedModule === basePackage || resolvedModule.startsWith(basePackage + ".")) {
333
+ resolved.add(resolvedModule);
334
+ }
335
+ } else if (imp === basePackage || imp.startsWith(basePackage + "/")) {
336
+ resolved.add(imp.replaceAll("/", "."));
337
+ }
338
+ }
339
+ return resolved;
340
+ }
341
+ function findSourceFiles(dir) {
342
+ const results = [];
343
+ function walk(d) {
344
+ const entries = readdirSync(d);
345
+ for (const entry of entries) {
346
+ if (entry === "node_modules" || entry === "dist" || entry === ".git") {
347
+ continue;
348
+ }
349
+ const full = join(d, entry);
350
+ const stat = statSync(full);
351
+ if (stat.isDirectory()) {
352
+ walk(full);
353
+ } else if (SUPPORTED_EXTENSIONS.has(extname2(entry))) {
354
+ results.push(full);
355
+ }
356
+ }
357
+ }
358
+ walk(dir);
359
+ return results;
360
+ }
361
+ function buildDependencyGraph(srcDir, basePackage) {
362
+ const graph = /* @__PURE__ */ new Map();
363
+ const files = findSourceFiles(srcDir);
364
+ for (const file of files) {
365
+ const moduleName = fileToModule(file, srcDir);
366
+ if (!moduleName) continue;
367
+ let source;
368
+ try {
369
+ source = readFileSync2(file, "utf-8");
370
+ } catch (e) {
371
+ console.error(`Warning: could not read ${file}: ${e}`);
372
+ continue;
373
+ }
374
+ let rawImports;
375
+ try {
376
+ rawImports = extractImports(source, file);
377
+ } catch (e) {
378
+ console.error(`Warning: could not parse ${file}: ${e}`);
379
+ continue;
380
+ }
381
+ const isPackage = INDEX_BASENAMES.has(basename(file));
382
+ const deps = resolveImports(rawImports, basePackage, moduleName, isPackage);
383
+ deps.delete(moduleName);
384
+ graph.set(moduleName, deps);
385
+ }
386
+ return graph;
387
+ }
388
+ function findCycles(graph) {
389
+ const WHITE = 0;
390
+ const GRAY = 1;
391
+ const BLACK = 2;
392
+ const color = /* @__PURE__ */ new Map();
393
+ const path2 = [];
394
+ const cycles = [];
395
+ function dfs(node) {
396
+ color.set(node, GRAY);
397
+ path2.push(node);
398
+ const neighbors = graph.get(node) ?? /* @__PURE__ */ new Set();
399
+ for (const neighbor of [...neighbors].sort()) {
400
+ if (color.get(neighbor) === GRAY && path2.includes(neighbor)) {
401
+ const idx = path2.indexOf(neighbor);
402
+ cycles.push([...path2.slice(idx), neighbor]);
403
+ } else if ((color.get(neighbor) ?? WHITE) === WHITE) {
404
+ dfs(neighbor);
405
+ }
406
+ }
407
+ path2.pop();
408
+ color.set(node, BLACK);
409
+ }
410
+ for (const node of [...graph.keys()].sort()) {
411
+ if ((color.get(node) ?? WHITE) === WHITE) {
412
+ dfs(node);
413
+ }
414
+ }
415
+ const unique = [];
416
+ const seen = /* @__PURE__ */ new Set();
417
+ for (const cycle of cycles) {
418
+ const ring = cycle.slice(0, -1);
419
+ let minVal = ring[0];
420
+ let minIdx = 0;
421
+ for (let i = 1; i < ring.length; i++) {
422
+ if (ring[i] < minVal) {
423
+ minVal = ring[i];
424
+ minIdx = i;
425
+ }
426
+ }
427
+ const normalized = [...ring.slice(minIdx), ...ring.slice(0, minIdx)].join(",");
428
+ if (!seen.has(normalized)) {
429
+ seen.add(normalized);
430
+ unique.push(cycle);
431
+ }
432
+ }
433
+ return unique;
434
+ }
435
+ function checkCircularImports(srcDir, basePackage) {
436
+ let stat;
437
+ try {
438
+ stat = statSync(srcDir);
439
+ } catch {
440
+ console.error(`Error: ${srcDir}/ directory not found`);
441
+ return 1;
442
+ }
443
+ if (!stat.isDirectory()) {
444
+ console.error(`Error: ${srcDir}/ is not a directory`);
445
+ return 1;
446
+ }
447
+ const graph = buildDependencyGraph(srcDir, basePackage);
448
+ console.log(`Scanned ${graph.size} modules`);
449
+ const cycles = findCycles(graph);
450
+ if (cycles.length > 0) {
451
+ console.log(`
452
+ Found ${cycles.length} circular import(s):
453
+ `);
454
+ for (let i = 0; i < cycles.length; i++) {
455
+ console.log(` Cycle ${i + 1}: ${cycles[i].join(" -> ")}`);
456
+ }
457
+ console.log();
458
+ return 1;
459
+ }
460
+ console.log("No circular imports detected.");
461
+ return 0;
462
+ }
463
+
464
+ // src/config.ts
465
+ import { readFileSync as readFileSync3 } from "fs";
466
+ import { join as join2 } from "path";
467
+ function loadConfig(projectDir) {
468
+ const dir = projectDir ?? process.cwd();
469
+ const pkgPath = join2(dir, "package.json");
470
+ let raw;
471
+ try {
472
+ raw = readFileSync3(pkgPath, "utf-8");
473
+ } catch {
474
+ return {};
475
+ }
476
+ const pkg = JSON.parse(raw);
477
+ const apdev = pkg["apdev"];
478
+ if (apdev && typeof apdev === "object" && !Array.isArray(apdev)) {
479
+ return apdev;
480
+ }
481
+ return {};
482
+ }
483
+
484
+ // src/index.ts
485
+ import { readFileSync as readFileSync4 } from "fs";
486
+ import { dirname, join as join3 } from "path";
487
+ import { fileURLToPath as fileURLToPath2 } from "url";
488
+ function readVersion() {
489
+ const thisDir = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath2(import.meta.url));
490
+ try {
491
+ const pkg = JSON.parse(readFileSync4(join3(thisDir, "..", "package.json"), "utf-8"));
492
+ return pkg.version ?? "0.0.0";
493
+ } catch {
494
+ return "0.0.0";
495
+ }
496
+ }
497
+ var version = readVersion();
498
+ export {
499
+ buildDependencyGraph,
500
+ checkCircularImports,
501
+ checkFile,
502
+ checkPaths,
503
+ fileToModule,
504
+ findCycles,
505
+ isAllowedChar,
506
+ loadConfig,
507
+ version
508
+ };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "apdev-js",
3
+ "version": "0.1.0",
4
+ "description": "Shared development tools for TypeScript/JavaScript projects - character validation, circular import detection, and more",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "bin": {
17
+ "apdev": "./dist/cli.js"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "release.sh"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "lint": "eslint src/ tests/",
28
+ "format": "prettier --write src/ tests/",
29
+ "apdev": "node dist/cli.js"
30
+ },
31
+ "keywords": [
32
+ "development-tools",
33
+ "character-validation",
34
+ "circular-imports",
35
+ "linting"
36
+ ],
37
+ "author": "aipartnerup <tercel.yi@gmail.com>",
38
+ "license": "Apache-2.0",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/aipartnerup/apdev",
42
+ "directory": "typescript"
43
+ },
44
+ "homepage": "https://aipartnerup.com",
45
+ "bugs": {
46
+ "url": "https://github.com/aipartnerup/apdev/issues"
47
+ },
48
+ "dependencies": {
49
+ "commander": "^13.0.0",
50
+ "typescript": "^5.7.0"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^22.0.0",
54
+ "tsup": "^8.0.0",
55
+ "vitest": "^3.0.0"
56
+ }
57
+ }