@ionify/ionify 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Khaled Salem
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/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # ⚡ Ionify
2
+
3
+ **A web infrastructure intelligence engine**
4
+
5
+ Ionify unifies development and production workflows into one persistent pipeline: dependency graph + content-addressable cache + hybrid transforms + analysis-ready architecture.
6
+
7
+ ---
8
+
9
+ ## What is Ionify?
10
+
11
+ Ionify is a web infrastructure intelligence engine.
12
+
13
+ Instead of treating development and production as separate tools, Ionify runs the entire lifecycle through a single persistent engine — from file watching and resolution to transformation, caching, and bundling.
14
+
15
+ At its core, Ionify maintains a **long-lived dependency graph** and a **content-addressable cache** that survive across runs. This allows the engine to understand how projects evolve over time, not just how they build once.
16
+
17
+ Ionify combines a high-performance native core with a hybrid transformation strategy:
18
+ - **OXC** as the primary engine for parsing and transformation
19
+ - **SWC** as a fallback to ensure compatibility and resilience
20
+
21
+ This unified and persistent design enables something traditional tooling cannot:
22
+ **infrastructure-level insight into the build process itself** — opening the door to analysis, optimization, and future AI-assisted recommendations.
23
+
24
+ ---
25
+
26
+ ## Why This Matters
27
+
28
+ ### What Ionify Unifies
29
+
30
+ Ionify unifies what is traditionally fragmented across multiple tools:
31
+
32
+ - Development server and production bundling
33
+ - Resolution logic and dependency semantics
34
+ - Transformation and caching strategies
35
+ - Performance characteristics across environments
36
+
37
+ By running everything through the same engine, Ionify eliminates an entire class of inconsistencies that appear when dev and build pipelines diverge.
38
+
39
+ ### For Developers
40
+
41
+ - **Fewer "works in dev, breaks in build" surprises** — same pipeline in both modes
42
+ - **Faster iteration** — persistent graph and cache reuse across runs
43
+ - **Deterministic behavior** — consistent across environments
44
+ - **Foundation for intelligent tooling** — infrastructure that can reason about builds, not just execute them
45
+
46
+ Ionify is designed to be the layer *below* frameworks and plugins — the infrastructure they can rely on.
47
+
48
+ ---
49
+
50
+ ## Architecture
51
+
52
+ ### Pipeline Overview
53
+
54
+ ```
55
+ Source Files
56
+
57
+ Resolver
58
+
59
+ Persistent Dependency Graph (native)
60
+
61
+ Transform Engine (OXC/SWC hybrid)
62
+
63
+ Content-Addressable Store (.ionify/cas/versionHash/moduleHash/...)
64
+
65
+ Dev Server / Bundler
66
+ ```
67
+
68
+ ### Hybrid Transformation Engine
69
+
70
+ Ionify uses a hybrid transformation strategy by design.
71
+
72
+ **OXC** is used as the primary engine for parsing and transformation, optimized for performance and modern JavaScript syntax. **SWC** acts as a fallback layer to ensure robustness and compatibility across edge cases and evolving ecosystems.
73
+
74
+ This approach allows Ionify to remain framework-agnostic while balancing speed, correctness, and long-term maintainability.
75
+
76
+ ### Storage
77
+
78
+ - **Graph persistence** — native Rust implementation
79
+ - **Transformed outputs** — stored in version-isolated CAS
80
+ - **Automatic invalidation** — via configuration hash
81
+
82
+ ### Why This Enables Intelligence
83
+
84
+ Because Ionify persists the dependency graph and transformed outputs, the engine can observe patterns over time:
85
+ - Which modules change frequently
86
+ - Which transformations are expensive
87
+ - How dependency structure affects rebuild cost
88
+
89
+ This data is the basis for future analyzer tooling and AI-assisted optimization.
90
+
91
+ ---
92
+
93
+ ## Quick Start
94
+
95
+ ### Installation
96
+
97
+ ```bash
98
+ pnpm add -D ionify
99
+ ```
100
+
101
+ ### Minimal Configuration
102
+
103
+ Create `ionify.config.ts`:
104
+
105
+ ```typescript
106
+ export default {
107
+ entry: "/src/main.ts",
108
+ outDir: "dist",
109
+ };
110
+ ```
111
+
112
+ ### Development Server
113
+
114
+ ```bash
115
+ pnpm ionify dev
116
+ ```
117
+
118
+ ### Production Build
119
+
120
+ ```bash
121
+ pnpm ionify build
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Project Status
127
+
128
+ **Core engine:** Stable and production-ready
129
+ **Unified dev + build pipeline:** Implemented
130
+ **Persistent graph and CAS:** In place
131
+ **Dependency pipeline:** Stabilization in progress
132
+ **Plugin system:** Temporarily paused
133
+ **Analyzer and AI layers:** Planned on top of the unified engine
134
+
135
+ ---
136
+
137
+ ## Key Features
138
+
139
+ ### Current
140
+
141
+ - **Persistent Graph Engine** — dependency graph saved to disk and reused across runs
142
+ - **Unified Pipeline** — dev and production share the same core logic
143
+ - **Content Addressable Cache (CAS)** — version-isolated, deterministic caching
144
+ - **Rust-Powered Performance** — native core for parsing, transformation, and bundling
145
+ - **Hybrid Transform Strategy** — OXC primary, SWC fallback
146
+ - **Graph-based HMR** — intelligent hot module replacement based on dependency structure
147
+
148
+ ### Planned
149
+
150
+ - **Analysis Dashboard** — visualize builds, cache hits, and dependency hot paths
151
+ - **AI-Assisted Optimization** — auto-tune splits, targets, and bundle strategies
152
+ - **Monorepo Support** — native workspace handling
153
+ - **Remote Build Cache** — team-level caching infrastructure
154
+
155
+ ---
156
+
157
+ ## Language Stack
158
+
159
+ | Component | Technology |
160
+ | ----------------------- | ------------------- |
161
+ | Core Engine | Rust |
162
+ | CLI / SDK / Plugin API | TypeScript |
163
+ | Graph Persistence | Native (sled/SQLite)|
164
+ | Primary Parser | OXC |
165
+ | Fallback Parser | SWC |
166
+ | Future Analyzer UI | React + TypeScript |
167
+
168
+ ---
169
+
170
+ ## Roadmap
171
+
172
+ 1. ✅ **Core Engine** — parser, graph, CAS, dev server
173
+ 2. ✅ **Unified Pipeline** — same engine for dev and production
174
+ 3. ✅ **Persistent Graph + Cache** — version-isolated storage
175
+ 4. 🔄 **Dependency Pipeline Stabilization** — robust node_modules handling
176
+ 5. ⏸️ **Plugin System** — paused for pipeline stabilization
177
+ 6. 📋 **Analyzer UI + Insights** — build visualization and metrics
178
+ 7. 📋 **AI Optimization Engine** — intelligent build recommendations
179
+ 8. 📋 **Monorepo / Remote Cache** — team collaboration features
180
+
181
+ ---
182
+
183
+ ## Philosophy
184
+
185
+ Ionify is designed to be the infrastructure layer that frameworks and plugins rely on — not another framework itself.
186
+
187
+ By unifying the build pipeline and persisting the dependency graph, Ionify creates a foundation for:
188
+ - Smarter tooling that understands your project over time
189
+ - Analysis and optimization based on real build patterns
190
+ - Future AI-assisted recommendations grounded in actual data
191
+
192
+ ---
193
+
194
+ ## Links
195
+
196
+ - **Website:** [ionify.cloud](https://ionify.cloud)
197
+ - **GitHub:** [github.com/khaledM-salem/Ionify](https://github.com/khaledM-salem/Ionify)
198
+ - **Issues:** [github.com/khaledM-salem/Ionify/issues](https://github.com/khaledM-salem/Ionify/issues)
199
+ - **Contact:** contact@ionify.cloud
200
+
201
+ ---
202
+
203
+ ## License
204
+
205
+ MIT © Khaled Salem
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // node_modules/.pnpm/tsup@8.5.1_@swc+core@1.15.4_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js
27
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
28
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
29
+
30
+ // src/cli/index.ts
31
+ var import_commander = require("commander");
32
+
33
+ // src/cli/utils/logger.ts
34
+ var import_chalk = __toESM(require("chalk"), 1);
35
+ function logInfo(message) {
36
+ console.log(import_chalk.default.cyan(`[Ionify] ${message}`));
37
+ }
38
+ function logError(message, err) {
39
+ console.error(import_chalk.default.red(`[Ionify] ${message}`));
40
+ if (err) console.error(err);
41
+ }
42
+
43
+ // src/cli/commands/analyze.ts
44
+ var import_fs2 = __toESM(require("fs"), 1);
45
+ var import_path2 = __toESM(require("path"), 1);
46
+
47
+ // src/native/index.ts
48
+ var import_fs = __toESM(require("fs"), 1);
49
+ var import_path = __toESM(require("path"), 1);
50
+ var import_module = require("module");
51
+
52
+ // src/core/version.ts
53
+ var import_node_crypto = require("crypto");
54
+
55
+ // src/native/index.ts
56
+ function resolveCandidates() {
57
+ const cwd = process.cwd();
58
+ const releaseDir = import_path.default.resolve(cwd, "target", "release");
59
+ const debugDir = import_path.default.resolve(cwd, "target", "debug");
60
+ const nativeDir = import_path.default.resolve(cwd, "native");
61
+ const moduleDir = import_path.default.dirname(new URL(importMetaUrl).pathname);
62
+ const packageNativeDir = import_path.default.resolve(moduleDir, "..", "native");
63
+ const packageDistDir = import_path.default.resolve(moduleDir, "..");
64
+ const platformFile = process.platform === "win32" ? "ionify_core.dll" : process.platform === "darwin" ? "libionify_core.dylib" : "libionify_core.so";
65
+ const candidates = [
66
+ // Installed package locations (checked first)
67
+ import_path.default.join(packageDistDir, "ionify_core.node"),
68
+ import_path.default.join(packageNativeDir, "ionify_core.node"),
69
+ // Development locations
70
+ import_path.default.join(nativeDir, "ionify_core.node"),
71
+ import_path.default.join(releaseDir, "ionify_core.node"),
72
+ import_path.default.join(releaseDir, platformFile),
73
+ import_path.default.join(debugDir, "ionify_core.node"),
74
+ import_path.default.join(debugDir, platformFile)
75
+ ];
76
+ return candidates.filter((candidate) => {
77
+ try {
78
+ return import_fs.default.existsSync(candidate) && import_fs.default.statSync(candidate).isFile();
79
+ } catch {
80
+ return false;
81
+ }
82
+ });
83
+ }
84
+ var nativeBinding = null;
85
+ (() => {
86
+ const require2 = (0, import_module.createRequire)(importMetaUrl);
87
+ for (const candidate of resolveCandidates()) {
88
+ try {
89
+ const mod = require2(candidate);
90
+ if (mod) {
91
+ nativeBinding = mod;
92
+ break;
93
+ }
94
+ } catch {
95
+ }
96
+ }
97
+ })();
98
+ var native = nativeBinding;
99
+
100
+ // src/cli/commands/analyze.ts
101
+ function readGraphFromDisk(root) {
102
+ const file = import_path2.default.join(root, ".ionify", "graph.json");
103
+ if (!import_fs2.default.existsSync(file)) return null;
104
+ try {
105
+ const raw = import_fs2.default.readFileSync(file, "utf8");
106
+ const snapshot = JSON.parse(raw);
107
+ if (snapshot?.version !== 1 || !snapshot?.nodes) return null;
108
+ return Object.entries(snapshot.nodes).map(([id, node]) => ({
109
+ id,
110
+ hash: node.hash ?? null,
111
+ deps: Array.isArray(node.deps) ? node.deps : []
112
+ }));
113
+ } catch (err) {
114
+ logError("Failed to read graph snapshot", err);
115
+ return null;
116
+ }
117
+ }
118
+ function computeSummary(nodes, limit = 10) {
119
+ const modules = nodes.length;
120
+ let edgeCount = 0;
121
+ const dependentCounts = /* @__PURE__ */ new Map();
122
+ for (const node of nodes) {
123
+ for (const dep of node.deps) {
124
+ edgeCount += 1;
125
+ dependentCounts.set(dep, (dependentCounts.get(dep) ?? 0) + 1);
126
+ }
127
+ }
128
+ const densest = [...nodes].sort((a, b) => b.deps.length - a.deps.length).slice(0, limit).map((node) => ({ id: node.id, deps: node.deps.length }));
129
+ const mostDepended = [...dependentCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, limit).map(([id, count]) => ({ id, dependents: count }));
130
+ const orphanSet = new Set(nodes.map((n) => n.id));
131
+ for (const node of nodes) {
132
+ for (const dep of node.deps) {
133
+ orphanSet.delete(dep);
134
+ }
135
+ }
136
+ return {
137
+ modules,
138
+ edges: edgeCount,
139
+ averageDeps: modules === 0 ? 0 : edgeCount / modules,
140
+ densest,
141
+ mostDepended,
142
+ orphans: Array.from(orphanSet)
143
+ };
144
+ }
145
+ async function loadGraphSnapshot() {
146
+ if (native?.graphLoad) {
147
+ try {
148
+ const nodes = native.graphLoad();
149
+ if (Array.isArray(nodes)) {
150
+ return nodes.map((node) => ({
151
+ id: node.id,
152
+ hash: node.hash ?? null,
153
+ deps: Array.isArray(node.deps) ? node.deps : []
154
+ }));
155
+ }
156
+ } catch (err) {
157
+ logError("Failed to load native graph snapshot", err);
158
+ }
159
+ }
160
+ return readGraphFromDisk(process.cwd());
161
+ }
162
+ async function runAnalyzeCommand(options = {}) {
163
+ const nodes = await loadGraphSnapshot();
164
+ if (!nodes || nodes.length === 0) {
165
+ logInfo("No cached graph found. Run `ionify dev` to generate dependency data.");
166
+ return;
167
+ }
168
+ const summary = computeSummary(nodes, options.limit ?? 10);
169
+ if (options.json) {
170
+ console.log(JSON.stringify(summary, null, 2));
171
+ return;
172
+ }
173
+ logInfo("Ionify Graph Summary");
174
+ console.log(` Modules: ${summary.modules}`);
175
+ console.log(` Dependencies: ${summary.edges}`);
176
+ console.log(` Avg deps / module: ${summary.averageDeps.toFixed(2)}`);
177
+ if (summary.densest.length > 0) {
178
+ console.log("\n Top modules by dependency count:");
179
+ for (const entry of summary.densest) {
180
+ console.log(` \u2022 ${entry.id} (${entry.deps})`);
181
+ }
182
+ }
183
+ if (summary.mostDepended.length > 0) {
184
+ console.log("\n Top modules by inbound dependents:");
185
+ for (const entry of summary.mostDepended) {
186
+ console.log(` \u2022 ${entry.id} (${entry.dependents})`);
187
+ }
188
+ }
189
+ if (summary.orphans.length) {
190
+ console.log("\n Orphan modules (no dependents):");
191
+ for (const file of summary.orphans.slice(0, options.limit ?? 10)) {
192
+ console.log(` \u2022 ${file}`);
193
+ }
194
+ if (summary.orphans.length > (options.limit ?? 10)) {
195
+ console.log(` \u2022 \u2026and ${summary.orphans.length - (options.limit ?? 10)} more`);
196
+ }
197
+ }
198
+ }
199
+
200
+ // src/cli/index.ts
201
+ var program = new import_commander.Command();
202
+ program.name("ionify").description("Ionify \u2013 Instant, Intelligent, Unified Build Engine").version("0.1.0");
203
+ program.command("dev").description("Start Ionify development server (coming in full release)").option("-p, --port <port>", "Port to run the server on", "5173").action(async () => {
204
+ logError("Dev server is not available in this release. Stay tuned!");
205
+ process.exit(1);
206
+ });
207
+ program.command("build").description("Build for production (coming in full release)").option("-o, --outDir <dir>", "Output directory", "dist").option("-l, --level <level>", "Optimization level (0-4)", "3").action(async () => {
208
+ logError("Build command is not available in this release. Stay tuned!");
209
+ process.exit(1);
210
+ });
211
+ program.command("analyze").description("Analyze bundle and performance").option("-f, --format <format>", "Output format (json|text)", "text").action(async (options) => {
212
+ try {
213
+ await runAnalyzeCommand(options);
214
+ } catch (err) {
215
+ logError(`Analyzer failed: ${err.message}`);
216
+ process.exit(1);
217
+ }
218
+ });
219
+ program.parse();
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/index.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/cli/utils/logger.ts
7
+ import chalk from "chalk";
8
+ function logInfo(message) {
9
+ console.log(chalk.cyan(`[Ionify] ${message}`));
10
+ }
11
+ function logError(message, err) {
12
+ console.error(chalk.red(`[Ionify] ${message}`));
13
+ if (err) console.error(err);
14
+ }
15
+
16
+ // src/cli/commands/analyze.ts
17
+ import fs2 from "fs";
18
+ import path2 from "path";
19
+
20
+ // src/native/index.ts
21
+ import fs from "fs";
22
+ import path from "path";
23
+ import { createRequire } from "module";
24
+
25
+ // src/core/version.ts
26
+ import { createHash } from "crypto";
27
+
28
+ // src/native/index.ts
29
+ function resolveCandidates() {
30
+ const cwd = process.cwd();
31
+ const releaseDir = path.resolve(cwd, "target", "release");
32
+ const debugDir = path.resolve(cwd, "target", "debug");
33
+ const nativeDir = path.resolve(cwd, "native");
34
+ const moduleDir = path.dirname(new URL(import.meta.url).pathname);
35
+ const packageNativeDir = path.resolve(moduleDir, "..", "native");
36
+ const packageDistDir = path.resolve(moduleDir, "..");
37
+ const platformFile = process.platform === "win32" ? "ionify_core.dll" : process.platform === "darwin" ? "libionify_core.dylib" : "libionify_core.so";
38
+ const candidates = [
39
+ // Installed package locations (checked first)
40
+ path.join(packageDistDir, "ionify_core.node"),
41
+ path.join(packageNativeDir, "ionify_core.node"),
42
+ // Development locations
43
+ path.join(nativeDir, "ionify_core.node"),
44
+ path.join(releaseDir, "ionify_core.node"),
45
+ path.join(releaseDir, platformFile),
46
+ path.join(debugDir, "ionify_core.node"),
47
+ path.join(debugDir, platformFile)
48
+ ];
49
+ return candidates.filter((candidate) => {
50
+ try {
51
+ return fs.existsSync(candidate) && fs.statSync(candidate).isFile();
52
+ } catch {
53
+ return false;
54
+ }
55
+ });
56
+ }
57
+ var nativeBinding = null;
58
+ (() => {
59
+ const require2 = createRequire(import.meta.url);
60
+ for (const candidate of resolveCandidates()) {
61
+ try {
62
+ const mod = require2(candidate);
63
+ if (mod) {
64
+ nativeBinding = mod;
65
+ break;
66
+ }
67
+ } catch {
68
+ }
69
+ }
70
+ })();
71
+ var native = nativeBinding;
72
+
73
+ // src/cli/commands/analyze.ts
74
+ function readGraphFromDisk(root) {
75
+ const file = path2.join(root, ".ionify", "graph.json");
76
+ if (!fs2.existsSync(file)) return null;
77
+ try {
78
+ const raw = fs2.readFileSync(file, "utf8");
79
+ const snapshot = JSON.parse(raw);
80
+ if (snapshot?.version !== 1 || !snapshot?.nodes) return null;
81
+ return Object.entries(snapshot.nodes).map(([id, node]) => ({
82
+ id,
83
+ hash: node.hash ?? null,
84
+ deps: Array.isArray(node.deps) ? node.deps : []
85
+ }));
86
+ } catch (err) {
87
+ logError("Failed to read graph snapshot", err);
88
+ return null;
89
+ }
90
+ }
91
+ function computeSummary(nodes, limit = 10) {
92
+ const modules = nodes.length;
93
+ let edgeCount = 0;
94
+ const dependentCounts = /* @__PURE__ */ new Map();
95
+ for (const node of nodes) {
96
+ for (const dep of node.deps) {
97
+ edgeCount += 1;
98
+ dependentCounts.set(dep, (dependentCounts.get(dep) ?? 0) + 1);
99
+ }
100
+ }
101
+ const densest = [...nodes].sort((a, b) => b.deps.length - a.deps.length).slice(0, limit).map((node) => ({ id: node.id, deps: node.deps.length }));
102
+ const mostDepended = [...dependentCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, limit).map(([id, count]) => ({ id, dependents: count }));
103
+ const orphanSet = new Set(nodes.map((n) => n.id));
104
+ for (const node of nodes) {
105
+ for (const dep of node.deps) {
106
+ orphanSet.delete(dep);
107
+ }
108
+ }
109
+ return {
110
+ modules,
111
+ edges: edgeCount,
112
+ averageDeps: modules === 0 ? 0 : edgeCount / modules,
113
+ densest,
114
+ mostDepended,
115
+ orphans: Array.from(orphanSet)
116
+ };
117
+ }
118
+ async function loadGraphSnapshot() {
119
+ if (native?.graphLoad) {
120
+ try {
121
+ const nodes = native.graphLoad();
122
+ if (Array.isArray(nodes)) {
123
+ return nodes.map((node) => ({
124
+ id: node.id,
125
+ hash: node.hash ?? null,
126
+ deps: Array.isArray(node.deps) ? node.deps : []
127
+ }));
128
+ }
129
+ } catch (err) {
130
+ logError("Failed to load native graph snapshot", err);
131
+ }
132
+ }
133
+ return readGraphFromDisk(process.cwd());
134
+ }
135
+ async function runAnalyzeCommand(options = {}) {
136
+ const nodes = await loadGraphSnapshot();
137
+ if (!nodes || nodes.length === 0) {
138
+ logInfo("No cached graph found. Run `ionify dev` to generate dependency data.");
139
+ return;
140
+ }
141
+ const summary = computeSummary(nodes, options.limit ?? 10);
142
+ if (options.json) {
143
+ console.log(JSON.stringify(summary, null, 2));
144
+ return;
145
+ }
146
+ logInfo("Ionify Graph Summary");
147
+ console.log(` Modules: ${summary.modules}`);
148
+ console.log(` Dependencies: ${summary.edges}`);
149
+ console.log(` Avg deps / module: ${summary.averageDeps.toFixed(2)}`);
150
+ if (summary.densest.length > 0) {
151
+ console.log("\n Top modules by dependency count:");
152
+ for (const entry of summary.densest) {
153
+ console.log(` \u2022 ${entry.id} (${entry.deps})`);
154
+ }
155
+ }
156
+ if (summary.mostDepended.length > 0) {
157
+ console.log("\n Top modules by inbound dependents:");
158
+ for (const entry of summary.mostDepended) {
159
+ console.log(` \u2022 ${entry.id} (${entry.dependents})`);
160
+ }
161
+ }
162
+ if (summary.orphans.length) {
163
+ console.log("\n Orphan modules (no dependents):");
164
+ for (const file of summary.orphans.slice(0, options.limit ?? 10)) {
165
+ console.log(` \u2022 ${file}`);
166
+ }
167
+ if (summary.orphans.length > (options.limit ?? 10)) {
168
+ console.log(` \u2022 \u2026and ${summary.orphans.length - (options.limit ?? 10)} more`);
169
+ }
170
+ }
171
+ }
172
+
173
+ // src/cli/index.ts
174
+ var program = new Command();
175
+ program.name("ionify").description("Ionify \u2013 Instant, Intelligent, Unified Build Engine").version("0.1.0");
176
+ program.command("dev").description("Start Ionify development server (coming in full release)").option("-p, --port <port>", "Port to run the server on", "5173").action(async () => {
177
+ logError("Dev server is not available in this release. Stay tuned!");
178
+ process.exit(1);
179
+ });
180
+ program.command("build").description("Build for production (coming in full release)").option("-o, --outDir <dir>", "Output directory", "dist").option("-l, --level <level>", "Optimization level (0-4)", "3").action(async () => {
181
+ logError("Build command is not available in this release. Stay tuned!");
182
+ process.exit(1);
183
+ });
184
+ program.command("analyze").description("Analyze bundle and performance").option("-f, --format <format>", "Output format (json|text)", "text").action(async (options) => {
185
+ try {
186
+ await runAnalyzeCommand(options);
187
+ } catch (err) {
188
+ logError(`Analyzer failed: ${err.message}`);
189
+ process.exit(1);
190
+ }
191
+ });
192
+ program.parse();