prodex 1.1.0 → 1.3.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.
Files changed (60) hide show
  1. package/README.md +234 -140
  2. package/bin/prodex.js +1 -17
  3. package/dist/cli/cli-input.js +106 -0
  4. package/dist/cli/flags.js +42 -0
  5. package/dist/cli/init.js +21 -0
  6. package/dist/cli/picker.js +82 -0
  7. package/dist/cli/summary.js +26 -0
  8. package/dist/constants/config-loader.js +95 -0
  9. package/dist/constants/config.js +26 -0
  10. package/dist/constants/default-config.js +34 -0
  11. package/dist/constants/render-constants.js +28 -0
  12. package/dist/core/combine.js +38 -0
  13. package/dist/core/dependency.js +55 -0
  14. package/dist/core/file-utils.js +41 -0
  15. package/dist/core/helpers.js +81 -0
  16. package/dist/core/output.js +48 -0
  17. package/dist/core/parsers/extract-imports.js +51 -0
  18. package/dist/core/renderers.js +42 -0
  19. package/dist/index.js +26 -0
  20. package/dist/lib/logger.js +14 -0
  21. package/dist/lib/polyfills.js +27 -0
  22. package/dist/lib/prompt.js +34 -0
  23. package/dist/lib/questions.js +28 -0
  24. package/dist/lib/utils.js +51 -0
  25. package/dist/resolvers/js/alias-loader.js +52 -0
  26. package/dist/resolvers/js/js-resolver.js +153 -0
  27. package/dist/resolvers/php/bindings.js +32 -0
  28. package/dist/resolvers/php/patterns.js +17 -0
  29. package/dist/resolvers/php/php-resolver.js +88 -0
  30. package/dist/resolvers/php/psr4.js +26 -0
  31. package/dist/resolvers/shared/excludes.js +11 -0
  32. package/dist/resolvers/shared/file-cache.js +29 -0
  33. package/dist/resolvers/shared/stats.js +17 -0
  34. package/dist/types/cli.types.js +12 -0
  35. package/dist/types/config.types.js +2 -0
  36. package/dist/types/core.types.js +2 -0
  37. package/dist/types/index.js +21 -0
  38. package/dist/types/resolver.types.js +2 -0
  39. package/dist/types/utils.types.js +2 -0
  40. package/package.json +16 -12
  41. package/dist/LICENSE +0 -21
  42. package/dist/README.md +0 -140
  43. package/dist/bin/prodex.js +0 -18
  44. package/dist/package.json +0 -45
  45. package/dist/src/cli/init.js +0 -18
  46. package/dist/src/cli/picker.js +0 -59
  47. package/dist/src/cli/summary.js +0 -6
  48. package/dist/src/constants/config-loader.js +0 -87
  49. package/dist/src/constants/config.js +0 -13
  50. package/dist/src/constants/default-config.js +0 -36
  51. package/dist/src/constants/render-constants.js +0 -22
  52. package/dist/src/core/alias-loader.js +0 -8
  53. package/dist/src/core/combine.js +0 -145
  54. package/dist/src/core/file-utils.js +0 -45
  55. package/dist/src/core/helpers.js +0 -77
  56. package/dist/src/core/renderers.js +0 -58
  57. package/dist/src/index.js +0 -15
  58. package/dist/src/resolvers/js-resolver.js +0 -180
  59. package/dist/src/resolvers/php-bindings.js +0 -31
  60. package/dist/src/resolvers/php-resolver.js +0 -155
package/dist/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Zeki
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/dist/README.md DELETED
@@ -1,140 +0,0 @@
1
- # 🧩 Prodex — Unified Project Indexer & Dependency Extractor
2
-
3
- > **Prodex** *(short for “Project Index”)* — a cross-language dependency combiner for modern full-stack applications.
4
- > Traverses **Laravel + React + TypeScript** projects to generate a single, organized view of your project’s true dependency scope.
5
-
6
- ---
7
-
8
- ## 🧠 Recent Fixes & Updates — v1.0.8
9
- - ⭐ **Priority Files Support** — priority files will now appear **first** on the entry selection list.
10
-
11
- - 🪟 **Windows path resolution fixed** — now uses proper `file://` URLs for full ESM compatibility.
12
- - 🧾 **Improved output naming** — automatic, context-aware filenames (e.g. `prodex-[entries]-combined.txt`).
13
- - ⚙️ **“Yes to all” confirmation added** — skip repetitive prompts during CLI runs.
14
-
15
- ---
16
-
17
- ---
18
-
19
- ## 📦 Installation
20
-
21
- ### Global install (recommended)
22
- ```bash
23
- npm install -g prodex
24
- ```
25
-
26
-
27
- ## 🚀 Features
28
-
29
- | Feature | Description |
30
- |----------|-------------|
31
- | ⚙️ **Cross-language resolver** | Parses JS/TS (`import`, `export`) and PHP (`use`, `require`, `include`) dependency trees. |
32
- | 🧭 **Alias detection** | Reads `tsconfig.json` and `vite.config.*` for alias paths (`@/components/...`). |
33
- | 🧩 **Laravel-aware** | Maps PSR-4 namespaces and detects providers under `app/Providers`. |
34
- | 🔄 **Recursive chain following** | Resolves dependency graphs up to a configurable depth and file limit. |
35
- | 🪶 **Clean unified output** | Merges all resolved files into a single `.txt` file with region markers for readability. |
36
- | 🧠 **Static & safe** | Fully static parsing — no runtime execution or file modification. |
37
- | 💬 **Interactive CLI** | Select files, confirm settings, or use “Yes to all” for streamlined automation. |
38
-
39
- ---
40
-
41
- ## ⚙️ Configuration
42
-
43
- Optional `.prodex.json` (in project root):
44
-
45
- ```json
46
- {
47
- "$schema": "https://raw.githubusercontent.com/emxhive/prodex/main/schema/prodex.schema.json",
48
- "output": "prodex",
49
- "scanDepth": 2,
50
- "limit": 200,
51
- "baseDirs": ["app", "routes", "resources/js"],
52
- "aliasOverrides": {
53
- "@hooks": "resources/js/hooks",
54
- "@data": "resources/js/data"
55
- },
56
- "entryExcludes": [
57
- "resources/js/components/ui/",
58
- "app/DTOs/"
59
- ],
60
- "importExcludes": [
61
- "node_modules",
62
- "@shadcn/"
63
- ],
64
- "priorityFiles": [
65
- "routes/web.php",
66
- "routes/api.php",
67
- "index.",
68
- "main.",
69
- "app."
70
- ]
71
- }
72
- ```
73
-
74
- ```
75
-
76
-
77
-
78
- ```
79
-
80
-
81
-
82
- Files are matched using `.includes()` (case-insensitive), so `"index."` will match `src/index.js`, `app/index.tsx`, etc.
83
- Popular entries appear at the top of the picker.
84
-
85
-
86
-
87
-
88
-
89
- ---
90
-
91
- ## 🧱 Example: Laravel + React
92
-
93
- ```bash
94
- prodex
95
- ```
96
-
97
- ```
98
- 🧩 Following dependency chain...
99
- ✅ prodex-app-routes-combined.txt written (24 file(s)).
100
- ```
101
-
102
- Included files:
103
- ```
104
- resources/js/pages/accounts.tsx
105
- app/Http/Controllers/Shots/AccountsController.php
106
- app/Repositories/Shots/FireflyApiRepository.php
107
- app/Enums/Shots/Granularity.php
108
- app/Support/Shots/CacheKeys.php
109
- ...
110
- ```
111
-
112
- ---
113
-
114
- ## 🧠 Ideal Use Cases
115
-
116
- - 📦 Generate single-file **project snapshots**
117
- - 🤖 Provide structured context for **AI assistants**
118
- - 🧩 Perform **dependency audits** or code reviews
119
- - 📄 Simplify documentation and onboarding
120
-
121
- ---
122
-
123
- ## 🔮 Upcoming Features
124
-
125
- - 📝 **Markdown export** (`.md`) with automatic code fences
126
- - 📦 **Configurable output formats** (txt / md)
127
- - ⚡ **Alias auto-discovery for Laravel Mix and Next.js**
128
-
129
- ---
130
-
131
- ## 🧾 License
132
-
133
- **MIT © 2025 [emxhive](https://github.com/emxhive)**
134
- Issues and contributions welcome:
135
- 👉 [github.com/emxhive/prodex/issues](https://github.com/emxhive/prodex/issues)
136
-
137
- ---
138
-
139
- **Prodex** — *Codebase, decoded*
140
-
@@ -1,18 +0,0 @@
1
- #!/usr/bin/env node
2
- import fs from "fs";
3
- import path from "path";
4
- import { pathToFileURL, fileURLToPath } from "url";
5
-
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
8
-
9
- const devPath = path.resolve(__dirname, "../src/index.js");
10
- const distPath = path.resolve(__dirname, "../dist/src/index.js");
11
-
12
- // prefer dist in published package
13
- const entry = fs.existsSync(distPath) ? distPath : devPath;
14
-
15
- // convert to file:// URL for cross-platform ESM loading
16
- const entryUrl = pathToFileURL(entry).href;
17
-
18
- import(entryUrl).then(({ default: startProdex }) => startProdex());
package/dist/package.json DELETED
@@ -1,45 +0,0 @@
1
- {
2
- "name": "prodex",
3
- "version": "1.1.0",
4
- "description": "Unified Project Indexer & Dependency Extractor for Laravel + React + Node stacks.",
5
- "type": "module",
6
- "bin": {
7
- "prodex": "./bin/prodex.js"
8
- },
9
- "main": "./dist/core/combine.js",
10
- "exports": {
11
- ".": "./dist/core/combine.js"
12
- },
13
- "files": [
14
- "dist/",
15
- "bin/",
16
- "README.md",
17
- "LICENSE"
18
- ],
19
- "keywords": [
20
- "laravel",
21
- "react",
22
- "typescript",
23
- "dependency",
24
- "analyzer",
25
- "cli",
26
- "node",
27
- "indexer"
28
- ],
29
- "scripts": {
30
- "clean": "rm -rf dist",
31
- "build": "npm run clean && node -e \"require('fs').mkdirSync('dist',{recursive:true})\" && cp -r src bin package.json README.md LICENSE dist/",
32
- "prepare": "npm run build"
33
- },
34
- "author": "emxhive",
35
- "license": "MIT",
36
- "devDependencies": {
37
- "@types/node": "^24.9.1",
38
- "tsup": "^8.5.0",
39
- "typescript": "^5.9.3"
40
- },
41
- "dependencies": {
42
- "inquirer": "^12.10.0",
43
- "micromatch": "^4.0.8"
44
- }
45
- }
@@ -1,18 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import { DEFAULT_PRODEX_CONFIG } from "../constants/default-config.js";
4
-
5
- export async function initProdex() {
6
- console.log("🪄 Prodex Init — Configuration Wizard (v2)\n");
7
-
8
- const dest = path.join(process.cwd(), "prodex.json");
9
-
10
- if (fs.existsSync(dest)) {
11
- console.log("❌ prodex.json already exists. Delete or modify it manually.\n");
12
- return;
13
- }
14
-
15
- fs.writeFileSync(dest, JSON.stringify(DEFAULT_PRODEX_CONFIG, null, 2) + "\n", "utf8");
16
- console.log(`✅ Created ${dest}`);
17
- console.log("💡 Globs supported everywhere (includes, excludes, priority).");
18
- }
@@ -1,59 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import inquirer from "inquirer";
4
- import { ROOT } from "../constants/config.js";
5
- import { walk, rel, sortWithPriority } from "../core/helpers.js";
6
-
7
- /**
8
- * Prodex v2 picker
9
- * - Keeps "Load more" (depth++)
10
- * - Removes manual path entry
11
- * - Uses cfg.entry.includes / cfg.entry.priority
12
- */
13
- export async function pickEntries(baseDirs, depth = 2, cfg = {}) {
14
- let selected = [];
15
-
16
- while (true) {
17
- const files = [];
18
-
19
- // Use an effective cfg that reflects the current depth for this iteration
20
- const effectiveCfg = { ...cfg, scanDepth: depth };
21
-
22
- for (const base of baseDirs) {
23
- const full = path.join(ROOT, base);
24
- if (!fs.existsSync(full)) continue;
25
- for (const f of walk(full, effectiveCfg, 0)) files.push(f);
26
- }
27
-
28
- // Priority-aware ordering
29
- const sorted = sortWithPriority(files, cfg.entry?.priority || []);
30
-
31
- // Build choices + the "Load more" control
32
- const choices = sorted.map(f => ({ name: rel(f), value: f }));
33
- choices.push(new inquirer.Separator());
34
- choices.push({ name: "🔽 Load more (go deeper)", value: "__loadmore" });
35
-
36
- const { picks } = await inquirer.prompt([
37
- {
38
- type: "checkbox",
39
- name: "picks",
40
- message: `Select entry files (depth ${depth})`,
41
- choices,
42
- loop: false,
43
- pageSize: 20,
44
- default: selected
45
- }
46
- ]);
47
-
48
- if (picks.includes("__loadmore")) {
49
- depth++;
50
- selected = picks.filter(p => p !== "__loadmore");
51
- continue;
52
- }
53
-
54
- selected = picks.filter(p => p !== "__loadmore");
55
- break;
56
- }
57
-
58
- return [...new Set(selected)];
59
- }
@@ -1,6 +0,0 @@
1
- export function showSummary({ outDir, fileName, entries }) {
2
- console.log("\n🧩 Active Run:");
3
- console.log(" • Output Directory:", outDir);
4
- console.log(" • File Name:", fileName);
5
- console.log(" • Entries:", entries.length);
6
- }
@@ -1,87 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import micromatch from "micromatch";
4
- import { DEFAULT_PRODEX_CONFIG } from "./default-config.js";
5
-
6
- export function loadProdexConfig() {
7
- const ROOT = process.cwd();
8
- const configPath = path.join(ROOT, "prodex.json");
9
-
10
- if (!fs.existsSync(configPath)) {
11
- console.log("🪄 No prodex.json found — generating default config...\n");
12
- fs.writeFileSync(configPath, JSON.stringify(DEFAULT_PRODEX_CONFIG, null, 2) + "\n", "utf8");
13
- }
14
- let raw;
15
- try {
16
- raw = JSON.parse(fs.readFileSync(configPath, "utf8"));
17
- } catch (err) {
18
- throw new Error(`❌ Invalid prodex.json: ${err.message}`);
19
- }
20
-
21
- const cfg = {
22
- outDir: path.resolve(ROOT, raw.outDir || "prodex"),
23
- scanDepth: raw.scanDepth ?? 2,
24
- limit: raw.limit ?? 200,
25
-
26
- entry: {
27
- includes: toArray(raw.entry?.includes ?? []),
28
- excludes: toArray(raw.entry?.excludes ?? []),
29
- priority: toArray(raw.entry?.priority ?? [])
30
- },
31
-
32
- imports: {
33
- includes: toArray(raw.imports?.includes ?? []),
34
- excludes: toArray(raw.imports?.excludes ?? []),
35
- aliases: raw.imports?.aliases ?? {}
36
- }
37
- };
38
-
39
- ensureDir(cfg.outDir);
40
-
41
- // // === Validation summary ===
42
- // console.log("🧩 Prodex Config Loaded\n");
43
- // console.log(" • outDir Dir:", cfg.outDir);
44
- // console.log(" • Entry Includes:", shortList(cfg.entry.includes));
45
- // console.log(" • Entry Excludes:", shortList(cfg.entry.excludes));
46
- // console.log(" • Import Includes:", shortList(cfg.imports.includes));
47
- // console.log(" • Import Excludes:", shortList(cfg.imports.excludes));
48
- // console.log(" • Aliases:", Object.keys(cfg.imports.aliases).length);
49
-
50
- return cfg;
51
- }
52
-
53
- /**
54
- * Utility — ensures array normalization.
55
- */
56
- function toArray(v) {
57
- return Array.isArray(v) ? v : v ? [v] : [];
58
- }
59
-
60
- /**
61
- * Utility — ensure directory exists.
62
- */
63
- function ensureDir(p) {
64
- try {
65
- fs.mkdirSync(p, { recursive: true });
66
- } catch {
67
- console.warn("⚠️ Could not create outDir directory:", p);
68
- }
69
- }
70
-
71
- /**
72
- * Utility — shortens list for display.
73
- */
74
- function shortList(list) {
75
- if (!list.length) return "(none)";
76
- return list.length > 3 ? list.slice(0, 3).join(", ") + "..." : list.join(", ");
77
- }
78
-
79
- /**
80
- * Glob matcher factory
81
- * Creates helpers for downstream modules.
82
- */
83
- export function makeGlobChecker(patterns) {
84
- const safe = toArray(patterns);
85
- if (!safe.length) return () => false;
86
- return (input) => micromatch.isMatch(input.replaceAll("\\", "/"), safe);
87
- }
@@ -1,13 +0,0 @@
1
- export const ROOT = process.cwd();
2
- export const CODE_EXTS = [".js", ".mjs", ".ts", ".tsx", ".d.ts", ".php"];
3
-
4
- import { resolveJsImports } from "../resolvers/js-resolver.js";
5
- import { resolvePhpImports } from "../resolvers/php-resolver.js";
6
-
7
- export const RESOLVERS = {
8
- ".php": resolvePhpImports,
9
- ".ts": resolveJsImports,
10
- ".tsx": resolveJsImports,
11
- ".d.ts": resolveJsImports,
12
- ".js": resolveJsImports
13
- };
@@ -1,36 +0,0 @@
1
- export const DEFAULT_PRODEX_CONFIG = {
2
- $schema: "https://raw.githubusercontent.com/emxhive/prodex/main/schema/prodex.schema.json",
3
- outDir: "prodex",
4
- scanDepth: 2,
5
- limit: 200,
6
- resolverDepth: 10,
7
-
8
- entry: {
9
- includes: ["app", "routes", "resources/js"],
10
- excludes: [
11
- "**/components/ui/**",
12
- "**/DTOs/**",
13
- "**/Enums/**"
14
- ],
15
- priority: [
16
- "**/routes/web.php",
17
- "**/routes/api.php",
18
- "**/*index.*",
19
- "**/*main.*",
20
- "**/app.*"
21
- ]
22
- },
23
-
24
- imports: {
25
- includes: ["**/*.d.ts", "**/*.interface.ts"],
26
- excludes: [
27
- "node_modules/**",
28
- "@shadcn/**",
29
- "**/components/ui/**"
30
- ],
31
- aliases: {
32
- "@hooks": "resources/js/hooks",
33
- "@data": "resources/js/data"
34
- }
35
- }
36
- };
@@ -1,22 +0,0 @@
1
- // ================================================================
2
- // 🧩 Prodex — Render Constants
3
- // Defines shared constants for renderer outDir formats.
4
- // ================================================================
5
-
6
- export const LANG_MAP = {
7
- ".js": "js",
8
- ".mjs": "js",
9
- ".jsx": "jsx",
10
- ".ts": "ts",
11
- ".tsx": "tsx",
12
- ".php": "php",
13
- ".json": "json",
14
- ".d.ts": "ts"
15
- };
16
-
17
- export const TEXT_HEADERS = {
18
- toc: "##==== Combined Scope ====",
19
- path: p => `##==== path: ${p} ====`,
20
- regionStart: p => `##region ${p}`,
21
- regionEnd: "##endregion"
22
- };
@@ -1,8 +0,0 @@
1
- export function loadJsAliases() {
2
- // will later handle vite + tsconfig
3
- return {};
4
- }
5
- export function loadComposerAliases() {
6
- // will later handle composer.json PSR-4
7
- return {};
8
- }
@@ -1,145 +0,0 @@
1
- import fs from "fs";
2
- import inquirer from "inquirer";
3
- import path from "path";
4
- import micromatch from "micromatch";
5
- import { pickEntries } from "../cli/picker.js";
6
- import { showSummary } from "../cli/summary.js";
7
- import { loadProdexConfig } from "../constants/config-loader.js";
8
- import { CODE_EXTS, RESOLVERS, ROOT } from "../constants/config.js";
9
- import { generateOutputName, resolveOutDirPath, safeMicromatchScan } from "./file-utils.js";
10
- import { renderMd, renderTxt, tocMd, tocTxt } from "./renderers.js";
11
-
12
-
13
- export async function runCombine(opts = {}) {
14
- const cliLimitFlag = process.argv.find(arg => arg.startsWith("--limit="));
15
- const customLimit = cliLimitFlag ? parseInt(cliLimitFlag.split("=")[1], 10) : null;
16
- const cliTxtFlag = process.argv.includes("--txt");
17
-
18
- const cfg = loadProdexConfig();
19
- const { scanDepth } = cfg;
20
-
21
- let entries = opts.entries;
22
-
23
- // 🧩 Headless mode: expand globs manually
24
- if (entries && entries.length) {
25
- const all = [];
26
- for (const pattern of entries) {
27
- const abs = path.resolve(process.cwd(), pattern);
28
- if (fs.existsSync(abs) && fs.statSync(abs).isFile()) {
29
- // direct file path (no glob)
30
- all.push(abs);
31
- continue;
32
- }
33
-
34
- // glob pattern
35
- const result = safeMicromatchScan(pattern, {
36
- cwd: process.cwd(),
37
- absolute: true,
38
- });
39
- if (result?.files?.length) all.push(...result.files);
40
- }
41
- entries = [...new Set(all)];
42
- } else {
43
- // fallback to interactive picker
44
- entries = await pickEntries(cfg.entry.includes, scanDepth, cfg);
45
- }
46
-
47
-
48
- if (!entries.length) {
49
- console.log("❌ No entries selected.");
50
- return;
51
- }
52
-
53
- console.log("\n📋 You selected:");
54
- for (const e of entries) console.log(" -", e.replace(ROOT + "/", ""));
55
-
56
- // 🧩 Auto name suggestion
57
- const autoName = generateOutputName(entries);
58
- const outDir = cfg.outDir || path.join(ROOT, "prodex");
59
- const limit = customLimit || cfg.limit || 200;
60
- const chain = true;
61
-
62
- // Skip prompt if entries were passed directly
63
- let outputBase = autoName;
64
- if (!opts.entries?.length) {
65
- const { outputBase: answer } = await inquirer.prompt([
66
- {
67
- type: "input",
68
- name: "outputBase",
69
- message: "Output file name (without extension):",
70
- default: autoName,
71
- filter: v => (v.trim() || autoName).replace(/[<>:"/\\|?*]+/g, "_"),
72
- },
73
- ]);
74
- outputBase = answer;
75
- }
76
-
77
- // Ensure output directory exists
78
- try {
79
- fs.mkdirSync(outDir, { recursive: true });
80
- } catch {
81
- console.warn("⚠️ Could not create outDir directory:", outDir);
82
- }
83
-
84
- const outputPath = resolveOutDirPath(outDir, outputBase, cliTxtFlag);
85
-
86
- showSummary({ outDir, fileName: path.basename(outputPath), entries });
87
-
88
-
89
- const result = chain ? await followChain(entries, cfg, limit) : { files: entries, stats: { totalImports: 0, totalResolved: 0 } };
90
- const sorted = [...result.files].sort((a, b) => a.localeCompare(b));
91
-
92
- const content = cliTxtFlag
93
- ? [tocTxt(sorted), ...sorted.map(renderTxt)].join("")
94
- : [tocMd(sorted), ...sorted.map((f, i) => renderMd(f, i === 0))].join("\n");
95
-
96
- fs.writeFileSync(outputPath, content, "utf8");
97
- console.log(
98
- `\n✅ ${outputPath}`
99
- );
100
- // 🧩 Print resolver summary (clean version)
101
- console.log(`\n🧩 Summary:
102
- • Unique imports expected: ${result.stats.expected.size}
103
- • Unique imports resolved: ${result.stats.resolved.size}
104
- `);
105
- }
106
-
107
- async function followChain(entryFiles, cfg, limit = 200) {
108
- console.log("🧩 Following dependency chain...");
109
- const visited = new Set();
110
- const all = [];
111
- const expected = new Set();
112
- const resolved = new Set();
113
- const resolverDepth = cfg.resolverDepth ?? 10;
114
-
115
- for (const f of entryFiles) {
116
- if (visited.has(f)) continue;
117
- all.push(f);
118
-
119
- const ext = path.extname(f);
120
- if (!CODE_EXTS.includes(ext)) continue;
121
-
122
- const resolver = RESOLVERS[ext];
123
- if (resolver) {
124
- const result = await resolver(f, cfg, visited, 0, resolverDepth);
125
- const { files, stats } = result;
126
- all.push(...files);
127
- stats?.expected?.forEach(x => expected.add(x));
128
- stats?.resolved?.forEach(x => resolved.add(x));
129
- }
130
-
131
- if (limit && all.length >= limit) {
132
- console.log("⚠️ Limit reached:", limit);
133
- break;
134
- }
135
- }
136
-
137
- return {
138
- files: [...new Set(all)],
139
- stats: {
140
- expected,
141
- resolved
142
- }
143
- };
144
- }
145
-
@@ -1,45 +0,0 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import micromatch from "micromatch";
4
-
5
- /**
6
- * Safe micromatch.scan wrapper (compatible with micromatch v4 & v5)
7
- */
8
- export function safeMicromatchScan(pattern, opts = {}) {
9
- const scanFn = micromatch.scan;
10
- if (typeof scanFn === "function") return scanFn(pattern, opts);
11
-
12
- // --- fallback for micromatch v4 ---
13
- const cwd = opts.cwd || process.cwd();
14
- const abs = !!opts.absolute;
15
- const allFiles = listAllFiles(cwd);
16
- const matched = micromatch.match(allFiles, pattern, { dot: true });
17
- return { files: abs ? matched.map(f => path.resolve(cwd, f)) : matched };
18
- }
19
-
20
- /**
21
- * Recursively list all files in a directory.
22
- * Used only for fallback (so performance isn’t critical).
23
- */
24
- function listAllFiles(dir) {
25
- const out = [];
26
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
27
- const full = path.join(dir, entry.name);
28
- if (entry.isDirectory()) out.push(...listAllFiles(full));
29
- else out.push(full);
30
- }
31
- return out;
32
- }
33
-
34
- export function generateOutputName(entries) {
35
- const names = entries.map(f => path.basename(f, path.extname(f)));
36
- if (names.length === 1) return names[0];
37
- if (names.length === 2) return `${names[0]}-${names[1]}`;
38
- if (names.length > 2) return `${names[0]}-and-${names.length - 1}more`;
39
- return "unknown";
40
- }
41
-
42
- export function resolveOutDirPath(outDir, base, asTxt = false) {
43
- const ext = asTxt ? "txt" : "md";
44
- return path.join(outDir, `${base}-combined.${ext}`);
45
- }