prodex 1.0.7 → 1.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  ---
7
7
 
8
- ## 🧠 Recent Fixes & Updates — v1.0.7
8
+ ## 🧠 Recent Fixes & Updates — v1.0.8
9
9
  - ⭐ **Priority Files Support** — priority files will now appear **first** on the entry selection list.
10
10
 
11
11
  - 🪟 **Windows path resolution fixed** — now uses proper `file://` URLs for full ESM compatibility.
package/dist/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  ---
7
7
 
8
- ## 🧠 Recent Fixes & Updates — v1.0.7
8
+ ## 🧠 Recent Fixes & Updates — v1.0.8
9
9
  - ⭐ **Priority Files Support** — priority files will now appear **first** on the entry selection list.
10
10
 
11
11
  - 🪟 **Windows path resolution fixed** — now uses proper `file://` URLs for full ESM compatibility.
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prodex",
3
- "version": "1.0.7",
3
+ "version": "1.1.0",
4
4
  "description": "Unified Project Indexer & Dependency Extractor for Laravel + React + Node stacks.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,10 +34,12 @@
34
34
  "author": "emxhive",
35
35
  "license": "MIT",
36
36
  "devDependencies": {
37
+ "@types/node": "^24.9.1",
37
38
  "tsup": "^8.5.0",
38
39
  "typescript": "^5.9.3"
39
40
  },
40
41
  "dependencies": {
41
- "inquirer": "^12.10.0"
42
+ "inquirer": "^12.10.0",
43
+ "micromatch": "^4.0.8"
42
44
  }
43
45
  }
@@ -1,50 +1,18 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import inquirer from "inquirer";
3
+ import { DEFAULT_PRODEX_CONFIG } from "../constants/default-config.js";
4
4
 
5
5
  export async function initProdex() {
6
- console.log("🪄 Prodex Init — Configuration Wizard\n");
6
+ console.log("🪄 Prodex Init — Configuration Wizard (v2)\n");
7
+
8
+ const dest = path.join(process.cwd(), "prodex.json");
7
9
 
8
- const dest = path.join(process.cwd(), ".prodex.json");
9
10
  if (fs.existsSync(dest)) {
10
- const { overwrite } = await inquirer.prompt([
11
- { type: "confirm", name: "overwrite", message: ".prodex.json already exists. Overwrite?", default: false }
12
- ]);
13
- if (!overwrite) {
14
- console.log("❌ Cancelled.");
15
- return;
16
- }
11
+ console.log("❌ prodex.json already exists. Delete or modify it manually.\n");
12
+ return;
17
13
  }
18
14
 
19
- const jsonc = `{
20
- "$schema": "https://raw.githubusercontent.com/emxhive/prodex/main/schema/prodex.schema.json",
21
- "output": "prodex",
22
- "scanDepth": 2,
23
- "limit": 200,
24
- "baseDirs": ["app", "routes", "resources/js"],
25
- "aliasOverrides": {
26
- "@hooks": "resources/js/hooks",
27
- "@data": "resources/js/data"
28
- },
29
- "priorityFiles": [
30
- "routes/web.php",
31
- "routes/api.php",
32
- "index.",
33
- "main.",
34
- "app."
35
- ],
36
- "entryExcludes": [
37
- "resources/js/components/ui/",
38
- "app/DTOs/"
39
- ],
40
- "importExcludes": [
41
- "node_modules",
42
- "@shadcn/"
43
- ]
44
- }
45
- `;
46
-
47
- fs.writeFileSync(dest, jsonc, "utf8");
15
+ fs.writeFileSync(dest, JSON.stringify(DEFAULT_PRODEX_CONFIG, null, 2) + "\n", "utf8");
48
16
  console.log(`✅ Created ${dest}`);
49
- console.log("💡 You can edit it anytime or rerun 'prodex init' to reset.");
17
+ console.log("💡 Globs supported everywhere (includes, excludes, priority).");
50
18
  }
@@ -4,33 +4,34 @@ import inquirer from "inquirer";
4
4
  import { ROOT } from "../constants/config.js";
5
5
  import { walk, rel, sortWithPriority } from "../core/helpers.js";
6
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
+ */
7
13
  export async function pickEntries(baseDirs, depth = 2, cfg = {}) {
8
14
  let selected = [];
15
+
9
16
  while (true) {
10
17
  const files = [];
18
+
19
+ // Use an effective cfg that reflects the current depth for this iteration
20
+ const effectiveCfg = { ...cfg, scanDepth: depth };
21
+
11
22
  for (const base of baseDirs) {
12
23
  const full = path.join(ROOT, base);
13
24
  if (!fs.existsSync(full)) continue;
14
- for (const f of walk(full, 0, depth)) files.push(f);
25
+ for (const f of walk(full, effectiveCfg, 0)) files.push(f);
15
26
  }
16
27
 
17
- const sorted = sortWithPriority(files, cfg.priorityFiles);
18
-
19
- const prioritized = sorted.filter(f =>
20
- cfg.priorityFiles?.some(p =>
21
- rel(f).replaceAll("\\", "/").toLowerCase().includes(p.toLowerCase())
22
- )
23
- );
28
+ // Priority-aware ordering
29
+ const sorted = sortWithPriority(files, cfg.entry?.priority || []);
24
30
 
25
- const choices = sorted.map(f => ({
26
- name: rel(f),
27
- value: f
28
- }));
29
-
30
- // if (prioritized.length) {
31
- // choices.unshift(new inquirer.Separator("⭐ Recommended entries"));
32
- // choices.splice(prioritized.length + 1, 0, new inquirer.Separator("─ Other files"));
33
- // }
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" });
34
35
 
35
36
  const { picks } = await inquirer.prompt([
36
37
  {
@@ -44,21 +45,15 @@ export async function pickEntries(baseDirs, depth = 2, cfg = {}) {
44
45
  }
45
46
  ]);
46
47
 
47
- if (picks.includes("__manual")) {
48
- const { manual } = await inquirer.prompt([
49
- { name: "manual", message: "Enter relative path:" }
50
- ]);
51
- if (manual.trim()) selected.push(path.resolve(ROOT, manual.trim()));
52
- }
53
-
54
48
  if (picks.includes("__loadmore")) {
55
49
  depth++;
56
- selected = picks.filter(p => !["__manual", "__loadmore"].includes(p));
50
+ selected = picks.filter(p => p !== "__loadmore");
57
51
  continue;
58
52
  }
59
53
 
60
- selected = picks.filter(p => !["__manual", "__loadmore"].includes(p));
54
+ selected = picks.filter(p => p !== "__loadmore");
61
55
  break;
62
56
  }
57
+
63
58
  return [...new Set(selected)];
64
59
  }
@@ -1,9 +1,6 @@
1
- export function showSummary({ outputDir, fileName, entries, scanDepth, limit, chain }) {
1
+ export function showSummary({ outDir, fileName, entries }) {
2
2
  console.log("\n🧩 Active Run:");
3
- console.log(" • Output Directory:", outputDir);
3
+ console.log(" • Output Directory:", outDir);
4
4
  console.log(" • File Name:", fileName);
5
5
  console.log(" • Entries:", entries.length);
6
- console.log(" • Scan Depth:", scanDepth);
7
- console.log(" • Limit:", limit);
8
- console.log(" • Chain:", chain ? "Enabled" : "Disabled");
9
6
  }
@@ -1,64 +1,87 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import {
4
- ROOT,
5
- CODE_EXTS,
6
- ENTRY_EXCLUDES,
7
- IMPORT_EXCLUDES,
8
- BASE_DIRS,
9
- PRIORITY_FILES
10
- } from "./config.js";
3
+ import micromatch from "micromatch";
4
+ import { DEFAULT_PRODEX_CONFIG } from "./default-config.js";
11
5
 
12
- /**
13
- * Loads and merges the Prodex configuration.
14
- * - `output` is treated strictly as a directory.
15
- * - Defaults to ROOT/prodex when not defined.
16
- */
17
6
  export function loadProdexConfig() {
18
- const configPath = path.join(ROOT, ".prodex.json");
19
- let userConfig = {};
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
+ },
20
31
 
21
- if (fs.existsSync(configPath)) {
22
- try {
23
- const data = fs.readFileSync(configPath, "utf8");
24
- userConfig = JSON.parse(data);
25
- console.log("? Loaded .prodex.json overrides");
26
- } catch (err) {
27
- console.warn("?? Failed to parse .prodex.json:", err.message);
32
+ imports: {
33
+ includes: toArray(raw.imports?.includes ?? []),
34
+ excludes: toArray(raw.imports?.excludes ?? []),
35
+ aliases: raw.imports?.aliases ?? {}
28
36
  }
29
- }
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
+ }
30
52
 
31
- const outputDir = userConfig.output
32
- ? path.resolve(ROOT, userConfig.output)
33
- : path.join(ROOT, "prodex");
53
+ /**
54
+ * Utility — ensures array normalization.
55
+ */
56
+ function toArray(v) {
57
+ return Array.isArray(v) ? v : v ? [v] : [];
58
+ }
34
59
 
60
+ /**
61
+ * Utility — ensure directory exists.
62
+ */
63
+ function ensureDir(p) {
35
64
  try {
36
- fs.mkdirSync(outputDir, { recursive: true });
37
- } catch (e) {
38
- console.warn("?? Could not create output directory:", outputDir);
65
+ fs.mkdirSync(p, { recursive: true });
66
+ } catch {
67
+ console.warn("⚠️ Could not create outDir directory:", p);
39
68
  }
69
+ }
40
70
 
41
- const merged = {
42
- output: outputDir,
43
- scanDepth: userConfig.scanDepth || 2,
44
- codeExts: userConfig.codeExts || CODE_EXTS,
45
- entryExcludes: [...ENTRY_EXCLUDES, ...(userConfig.entryExcludes || [])],
46
- importExcludes: [...IMPORT_EXCLUDES, ...(userConfig.importExcludes || [])],
47
- baseDirs: [...new Set([...(userConfig.baseDirs || []), ...BASE_DIRS])],
48
- aliasOverrides: userConfig.aliasOverrides || {},
49
- limit: userConfig.limit || 200,
50
- priorityFiles: userConfig.priorityFiles || PRIORITY_FILES
51
- };
52
-
53
- // console.log("?? Active Config:");
54
- // console.log(" • Output Directory:", merged.output);
55
- // console.log(" • Scan Depth:", merged.scanDepth);
56
- // console.log(" • Base Dirs:", merged.baseDirs.join(", "));
57
- // if (userConfig.entryExcludes || userConfig.importExcludes)
58
- // console.log(" • Custom Exclusions:", {
59
- // entries: userConfig.entryExcludes?.length || 0,
60
- // imports: userConfig.importExcludes?.length || 0
61
- // });
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
+ }
62
78
 
63
- return merged;
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);
64
87
  }
@@ -1,45 +1,9 @@
1
- import { resolveJsImports } from "../resolvers/js-resolver.js";
2
- import { resolvePhpImports } from "../resolvers/php-resolver.js";
3
-
4
-
5
1
  export const ROOT = process.cwd();
6
-
7
2
  export const CODE_EXTS = [".js", ".mjs", ".ts", ".tsx", ".d.ts", ".php"];
8
- export const ENTRY_EXCLUDES = [
9
- "resources/js/components/ui/",
10
- "app/Enums/",
11
- "app/DTOs/",
12
- "app/Models/",
13
- "app/Data/",
14
- "resources/js/wayfinder/",
15
- "resources/js/routes/",
16
- "resources/js/actions/",
17
- "resources/js/hooks/"
18
- ];
19
- export const IMPORT_EXCLUDES = [
20
- "node_modules",
21
- "@shadcn/",
22
- "@/components/ui/",
23
- "@components/ui/",
24
- "resources/js/components/ui/",
25
- "resources/js/hooks/",
26
- "resources/js/wayfinder/",
27
- "resources/js/routes/",
28
- "resources/js/actions/"
29
- ];
30
- export const BASE_DIRS = ["src", "bin", "schema", "app", "routes", "resources/js"];
31
3
 
32
- export const PRIORITY_FILES = [
33
- "routes/web.php",
34
- "routes/api.php",
35
- "index.",
36
- "main.",
37
- "app."
38
- ]
39
- /**
40
- * Resolver map — links file extensions to their resolver functions.
41
- * Extend this to support new formats (.vue, .jsx, etc.).
42
- */
4
+ import { resolveJsImports } from "../resolvers/js-resolver.js";
5
+ import { resolvePhpImports } from "../resolvers/php-resolver.js";
6
+
43
7
  export const RESOLVERS = {
44
8
  ".php": resolvePhpImports,
45
9
  ".ts": resolveJsImports,
@@ -47,44 +11,3 @@ export const RESOLVERS = {
47
11
  ".d.ts": resolveJsImports,
48
12
  ".js": resolveJsImports
49
13
  };
50
-
51
- /**
52
- * Prompt definitions used by Inquirer in combine.js.
53
- * These are constants to keep UI consistent across releases.
54
- */
55
- export const PROMPTS = {
56
- yesToAll: {
57
- type: "confirm",
58
- name: "yesToAll",
59
- message: "Proceed automatically with default settings (Yes to all)?",
60
- default: true
61
- },
62
- combine: [
63
- {
64
- type: "input",
65
- name: "outputBase",
66
- message: "Output base name (without extension):",
67
- default: null, // will be set dynamically
68
- filter: v => v.trim()
69
- },
70
- {
71
- type: "number",
72
- name: "limit",
73
- message: "Limit number of merged files:",
74
- default: 200, // will be overridden at runtime
75
- validate: v => (!isNaN(v) && v > 0) || "Enter a valid positive number"
76
- },
77
- {
78
- type: "confirm",
79
- name: "chain",
80
- message: "Follow dependency chain?",
81
- default: true
82
- },
83
- {
84
- type: "confirm",
85
- name: "proceed",
86
- message: "Proceed with combine?",
87
- default: true
88
- }
89
- ]
90
- };
@@ -0,0 +1,36 @@
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
+ };
@@ -0,0 +1,22 @@
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
+ };