@ionify/ionify 0.1.0 → 0.1.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 +2 -2
- package/dist/cli/index.cjs +238 -13
- package/dist/cli/index.js +238 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -194,8 +194,8 @@ By unifying the build pipeline and persisting the dependency graph, Ionify creat
|
|
|
194
194
|
## Links
|
|
195
195
|
|
|
196
196
|
- **Website:** [ionify.cloud](https://ionify.cloud)
|
|
197
|
-
- **GitHub:** [github.com/
|
|
198
|
-
- **Issues:** [github.com/
|
|
197
|
+
- **GitHub:** [github.com/ionifyjs/ionify](https://github.com/ionifyjs/ionify)
|
|
198
|
+
- **Issues:** [github.com/ionifyjs/ionify/issues](https://github.com/ionifyjs/ionify/issues)
|
|
199
199
|
- **Contact:** contact@ionify.cloud
|
|
200
200
|
|
|
201
201
|
---
|
package/dist/cli/index.cjs
CHANGED
|
@@ -40,10 +40,6 @@ function logError(message, err) {
|
|
|
40
40
|
if (err) console.error(err);
|
|
41
41
|
}
|
|
42
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
43
|
// src/native/index.ts
|
|
48
44
|
var import_fs = __toESM(require("fs"), 1);
|
|
49
45
|
var import_path = __toESM(require("path"), 1);
|
|
@@ -97,12 +93,233 @@ var nativeBinding = null;
|
|
|
97
93
|
})();
|
|
98
94
|
var native = nativeBinding;
|
|
99
95
|
|
|
96
|
+
// src/cli/utils/config.ts
|
|
97
|
+
var import_fs2 = __toESM(require("fs"), 1);
|
|
98
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
99
|
+
var import_url = require("url");
|
|
100
|
+
var import_esbuild = require("esbuild");
|
|
101
|
+
|
|
102
|
+
// src/core/resolver.ts
|
|
103
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
104
|
+
var import_module2 = require("module");
|
|
105
|
+
var swc = null;
|
|
106
|
+
(() => {
|
|
107
|
+
try {
|
|
108
|
+
const require2 = (0, import_module2.createRequire)(importMetaUrl);
|
|
109
|
+
swc = require2("@swc/core");
|
|
110
|
+
} catch {
|
|
111
|
+
swc = null;
|
|
112
|
+
}
|
|
113
|
+
})();
|
|
114
|
+
var customAliasEntries = [];
|
|
115
|
+
function createAliasEntry(pattern, targets) {
|
|
116
|
+
const hasWildcard = pattern.includes("*");
|
|
117
|
+
if (hasWildcard) {
|
|
118
|
+
const escaped = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&");
|
|
119
|
+
const matcher = new RegExp(`^${escaped.replace(/\*/g, "(.*)")}$`);
|
|
120
|
+
return {
|
|
121
|
+
resolveCandidates(specifier) {
|
|
122
|
+
const match = matcher.exec(specifier);
|
|
123
|
+
if (!match) return [];
|
|
124
|
+
const wildcards = match.slice(1);
|
|
125
|
+
return targets.map((target) => {
|
|
126
|
+
if (!target.includes("*")) return target;
|
|
127
|
+
const segments = target.split("*");
|
|
128
|
+
let rebuilt = segments[0] ?? "";
|
|
129
|
+
for (let i = 1; i < segments.length; i++) {
|
|
130
|
+
const replacement = wildcards[i - 1] ?? wildcards[wildcards.length - 1] ?? "";
|
|
131
|
+
rebuilt += replacement + segments[i];
|
|
132
|
+
}
|
|
133
|
+
return rebuilt;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const normalizedPattern = pattern.endsWith("/") ? pattern.slice(0, -1) : pattern;
|
|
139
|
+
return {
|
|
140
|
+
resolveCandidates(specifier) {
|
|
141
|
+
if (specifier === normalizedPattern) {
|
|
142
|
+
return targets;
|
|
143
|
+
}
|
|
144
|
+
if (normalizedPattern && specifier.startsWith(normalizedPattern + "/")) {
|
|
145
|
+
const remainder = specifier.slice(normalizedPattern.length + 1);
|
|
146
|
+
return targets.map((target) => import_path2.default.join(target, remainder));
|
|
147
|
+
}
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function buildAliasEntries(aliases, baseDir) {
|
|
153
|
+
const entries = [];
|
|
154
|
+
for (const [pattern, value] of Object.entries(aliases)) {
|
|
155
|
+
const replacements = Array.isArray(value) ? value : [value];
|
|
156
|
+
const targets = replacements.filter((rep) => typeof rep === "string" && rep.trim().length > 0).map(
|
|
157
|
+
(rep) => import_path2.default.isAbsolute(rep) ? rep : import_path2.default.resolve(baseDir, rep)
|
|
158
|
+
);
|
|
159
|
+
if (!targets.length) continue;
|
|
160
|
+
entries.push(createAliasEntry(pattern, targets));
|
|
161
|
+
}
|
|
162
|
+
return entries;
|
|
163
|
+
}
|
|
164
|
+
function configureResolverAliases(aliases, baseDir) {
|
|
165
|
+
customAliasEntries = aliases ? buildAliasEntries(aliases, baseDir) : [];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/cli/utils/config.ts
|
|
169
|
+
var CONFIG_BASENAMES = [
|
|
170
|
+
"ionify.config.ts",
|
|
171
|
+
"ionify.config.mts",
|
|
172
|
+
"ionify.config.js",
|
|
173
|
+
"ionify.config.mjs",
|
|
174
|
+
"ionify.config.cjs"
|
|
175
|
+
];
|
|
176
|
+
var cachedConfig = null;
|
|
177
|
+
var configLoaded = false;
|
|
178
|
+
async function bundleConfig(entry) {
|
|
179
|
+
const absDir = import_path3.default.dirname(entry);
|
|
180
|
+
const inlineIonifyPlugin = {
|
|
181
|
+
name: "inline-ionify",
|
|
182
|
+
setup(build2) {
|
|
183
|
+
build2.onResolve({ filter: /^ionify$/ }, () => ({
|
|
184
|
+
path: "ionify-virtual",
|
|
185
|
+
namespace: "ionify-ns"
|
|
186
|
+
}));
|
|
187
|
+
build2.onLoad({ filter: /.*/, namespace: "ionify-ns" }, () => ({
|
|
188
|
+
contents: `
|
|
189
|
+
export function defineConfig(config) {
|
|
190
|
+
return typeof config === 'function' ? config : () => config;
|
|
191
|
+
}
|
|
192
|
+
`,
|
|
193
|
+
loader: "js"
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
const result = await (0, import_esbuild.build)({
|
|
198
|
+
entryPoints: [entry],
|
|
199
|
+
bundle: true,
|
|
200
|
+
platform: "node",
|
|
201
|
+
format: "esm",
|
|
202
|
+
sourcemap: "inline",
|
|
203
|
+
write: false,
|
|
204
|
+
target: "node18",
|
|
205
|
+
logLevel: "silent",
|
|
206
|
+
absWorkingDir: absDir,
|
|
207
|
+
plugins: [inlineIonifyPlugin]
|
|
208
|
+
});
|
|
209
|
+
const output = result.outputFiles?.[0];
|
|
210
|
+
if (!output) throw new Error("Failed to bundle ionify config");
|
|
211
|
+
const dirnameLiteral = JSON.stringify(absDir);
|
|
212
|
+
const filenameLiteral = JSON.stringify(entry);
|
|
213
|
+
const importMetaLiteral = JSON.stringify((0, import_url.pathToFileURL)(entry).href);
|
|
214
|
+
let contents = output.text;
|
|
215
|
+
if (contents.includes("import.meta.url")) {
|
|
216
|
+
contents = contents.replace(/import\.meta\.url/g, "__IONIFY_IMPORT_META_URL");
|
|
217
|
+
contents = `const __IONIFY_IMPORT_META_URL = ${importMetaLiteral};
|
|
218
|
+
${contents}`;
|
|
219
|
+
}
|
|
220
|
+
const preamble = `const __dirname = ${dirnameLiteral};
|
|
221
|
+
const __filename = ${filenameLiteral};
|
|
222
|
+
`;
|
|
223
|
+
return preamble + contents;
|
|
224
|
+
}
|
|
225
|
+
function findConfigFile(cwd) {
|
|
226
|
+
for (const name of CONFIG_BASENAMES) {
|
|
227
|
+
const candidate = import_path3.default.resolve(cwd, name);
|
|
228
|
+
if (import_fs2.default.existsSync(candidate) && import_fs2.default.statSync(candidate).isFile()) {
|
|
229
|
+
return candidate;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
async function loadIonifyConfig(cwd = process.cwd()) {
|
|
235
|
+
if (configLoaded) return cachedConfig;
|
|
236
|
+
configLoaded = true;
|
|
237
|
+
const configPath = findConfigFile(cwd);
|
|
238
|
+
if (!configPath) {
|
|
239
|
+
cachedConfig = null;
|
|
240
|
+
configureResolverAliases(void 0, cwd);
|
|
241
|
+
return cachedConfig;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const bundled = await bundleConfig(configPath);
|
|
245
|
+
const dataUrl = `data:text/javascript;base64,${Buffer.from(bundled).toString("base64")}`;
|
|
246
|
+
const imported = await import(dataUrl);
|
|
247
|
+
let resolved = imported?.default ?? imported?.config ?? imported ?? null;
|
|
248
|
+
if (resolved && typeof resolved === "function") {
|
|
249
|
+
resolved = resolved({ mode: process.env.NODE_ENV || "development" });
|
|
250
|
+
}
|
|
251
|
+
if (resolved && typeof resolved?.then === "function") {
|
|
252
|
+
resolved = await resolved;
|
|
253
|
+
}
|
|
254
|
+
if (resolved && typeof resolved === "object") {
|
|
255
|
+
cachedConfig = resolved;
|
|
256
|
+
const baseDir = import_path3.default.dirname(configPath);
|
|
257
|
+
const aliases = resolved?.resolve?.alias;
|
|
258
|
+
if (aliases && typeof aliases === "object") {
|
|
259
|
+
configureResolverAliases(aliases, baseDir);
|
|
260
|
+
} else {
|
|
261
|
+
configureResolverAliases(void 0, baseDir);
|
|
262
|
+
}
|
|
263
|
+
logInfo(`Loaded ionify config from ${import_path3.default.relative(cwd, configPath)}`);
|
|
264
|
+
} else {
|
|
265
|
+
throw new Error("Config did not export an object");
|
|
266
|
+
}
|
|
267
|
+
} catch (err) {
|
|
268
|
+
logError("Failed to load ionify.config", err);
|
|
269
|
+
cachedConfig = null;
|
|
270
|
+
configureResolverAliases(void 0, cwd);
|
|
271
|
+
}
|
|
272
|
+
return cachedConfig;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/cli/commands/dev.ts
|
|
276
|
+
async function startDevServer(options = {}) {
|
|
277
|
+
try {
|
|
278
|
+
const config = await loadIonifyConfig();
|
|
279
|
+
const port = parseInt(options.port || config?.server?.port || "5173");
|
|
280
|
+
logInfo(`Starting Ionify dev server on port ${port}...`);
|
|
281
|
+
const result = native.startDevServer({
|
|
282
|
+
port,
|
|
283
|
+
root: process.cwd(),
|
|
284
|
+
config: config || {}
|
|
285
|
+
});
|
|
286
|
+
logInfo(`Dev server running at http://localhost:${port}`);
|
|
287
|
+
return result;
|
|
288
|
+
} catch (err) {
|
|
289
|
+
logError(`Dev server failed: ${err.message}`);
|
|
290
|
+
throw err;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/cli/commands/build.ts
|
|
295
|
+
async function runBuildCommand(options = {}) {
|
|
296
|
+
try {
|
|
297
|
+
const config = await loadIonifyConfig();
|
|
298
|
+
const outDir = options.outDir || config?.build?.outDir || "dist";
|
|
299
|
+
const level = options.level ?? config?.optimizationLevel ?? 3;
|
|
300
|
+
logInfo(`Building for production (optimization level: ${level})...`);
|
|
301
|
+
const result = native.build({
|
|
302
|
+
root: process.cwd(),
|
|
303
|
+
outDir,
|
|
304
|
+
optimizationLevel: level,
|
|
305
|
+
config: config || {}
|
|
306
|
+
});
|
|
307
|
+
logInfo(`\u2705 Build complete! Output: ${outDir}/`);
|
|
308
|
+
return result;
|
|
309
|
+
} catch (err) {
|
|
310
|
+
logError(`Build failed: ${err.message}`);
|
|
311
|
+
throw err;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
100
315
|
// src/cli/commands/analyze.ts
|
|
316
|
+
var import_fs3 = __toESM(require("fs"), 1);
|
|
317
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
101
318
|
function readGraphFromDisk(root) {
|
|
102
|
-
const file =
|
|
103
|
-
if (!
|
|
319
|
+
const file = import_path4.default.join(root, ".ionify", "graph.json");
|
|
320
|
+
if (!import_fs3.default.existsSync(file)) return null;
|
|
104
321
|
try {
|
|
105
|
-
const raw =
|
|
322
|
+
const raw = import_fs3.default.readFileSync(file, "utf8");
|
|
106
323
|
const snapshot = JSON.parse(raw);
|
|
107
324
|
if (snapshot?.version !== 1 || !snapshot?.nodes) return null;
|
|
108
325
|
return Object.entries(snapshot.nodes).map(([id, node]) => ({
|
|
@@ -200,13 +417,21 @@ async function runAnalyzeCommand(options = {}) {
|
|
|
200
417
|
// src/cli/index.ts
|
|
201
418
|
var program = new import_commander.Command();
|
|
202
419
|
program.name("ionify").description("Ionify \u2013 Instant, Intelligent, Unified Build Engine").version("0.1.0");
|
|
203
|
-
program.command("dev").description("Start Ionify development server
|
|
204
|
-
|
|
205
|
-
|
|
420
|
+
program.command("dev").description("Start Ionify development server").option("-p, --port <port>", "Port to run the server on", "5173").action(async (options) => {
|
|
421
|
+
try {
|
|
422
|
+
await startDevServer(options);
|
|
423
|
+
} catch (err) {
|
|
424
|
+
logError(`Dev server failed: ${err.message}`);
|
|
425
|
+
process.exit(1);
|
|
426
|
+
}
|
|
206
427
|
});
|
|
207
|
-
program.command("build").description("Build for production
|
|
208
|
-
|
|
209
|
-
|
|
428
|
+
program.command("build").description("Build for production").option("-o, --outDir <dir>", "Output directory", "dist").option("-l, --level <level>", "Optimization level (0-4)", "3").action(async (options) => {
|
|
429
|
+
try {
|
|
430
|
+
await runBuildCommand(options);
|
|
431
|
+
} catch (err) {
|
|
432
|
+
logError(`Build failed: ${err.message}`);
|
|
433
|
+
process.exit(1);
|
|
434
|
+
}
|
|
210
435
|
});
|
|
211
436
|
program.command("analyze").description("Analyze bundle and performance").option("-f, --format <format>", "Output format (json|text)", "text").action(async (options) => {
|
|
212
437
|
try {
|
package/dist/cli/index.js
CHANGED
|
@@ -13,10 +13,6 @@ function logError(message, err) {
|
|
|
13
13
|
if (err) console.error(err);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
// src/cli/commands/analyze.ts
|
|
17
|
-
import fs2 from "fs";
|
|
18
|
-
import path2 from "path";
|
|
19
|
-
|
|
20
16
|
// src/native/index.ts
|
|
21
17
|
import fs from "fs";
|
|
22
18
|
import path from "path";
|
|
@@ -70,12 +66,233 @@ var nativeBinding = null;
|
|
|
70
66
|
})();
|
|
71
67
|
var native = nativeBinding;
|
|
72
68
|
|
|
69
|
+
// src/cli/utils/config.ts
|
|
70
|
+
import fs2 from "fs";
|
|
71
|
+
import path3 from "path";
|
|
72
|
+
import { pathToFileURL } from "url";
|
|
73
|
+
import { build } from "esbuild";
|
|
74
|
+
|
|
75
|
+
// src/core/resolver.ts
|
|
76
|
+
import path2 from "path";
|
|
77
|
+
import { createRequire as createRequire2 } from "module";
|
|
78
|
+
var swc = null;
|
|
79
|
+
(() => {
|
|
80
|
+
try {
|
|
81
|
+
const require2 = createRequire2(import.meta.url);
|
|
82
|
+
swc = require2("@swc/core");
|
|
83
|
+
} catch {
|
|
84
|
+
swc = null;
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
var customAliasEntries = [];
|
|
88
|
+
function createAliasEntry(pattern, targets) {
|
|
89
|
+
const hasWildcard = pattern.includes("*");
|
|
90
|
+
if (hasWildcard) {
|
|
91
|
+
const escaped = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&");
|
|
92
|
+
const matcher = new RegExp(`^${escaped.replace(/\*/g, "(.*)")}$`);
|
|
93
|
+
return {
|
|
94
|
+
resolveCandidates(specifier) {
|
|
95
|
+
const match = matcher.exec(specifier);
|
|
96
|
+
if (!match) return [];
|
|
97
|
+
const wildcards = match.slice(1);
|
|
98
|
+
return targets.map((target) => {
|
|
99
|
+
if (!target.includes("*")) return target;
|
|
100
|
+
const segments = target.split("*");
|
|
101
|
+
let rebuilt = segments[0] ?? "";
|
|
102
|
+
for (let i = 1; i < segments.length; i++) {
|
|
103
|
+
const replacement = wildcards[i - 1] ?? wildcards[wildcards.length - 1] ?? "";
|
|
104
|
+
rebuilt += replacement + segments[i];
|
|
105
|
+
}
|
|
106
|
+
return rebuilt;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const normalizedPattern = pattern.endsWith("/") ? pattern.slice(0, -1) : pattern;
|
|
112
|
+
return {
|
|
113
|
+
resolveCandidates(specifier) {
|
|
114
|
+
if (specifier === normalizedPattern) {
|
|
115
|
+
return targets;
|
|
116
|
+
}
|
|
117
|
+
if (normalizedPattern && specifier.startsWith(normalizedPattern + "/")) {
|
|
118
|
+
const remainder = specifier.slice(normalizedPattern.length + 1);
|
|
119
|
+
return targets.map((target) => path2.join(target, remainder));
|
|
120
|
+
}
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function buildAliasEntries(aliases, baseDir) {
|
|
126
|
+
const entries = [];
|
|
127
|
+
for (const [pattern, value] of Object.entries(aliases)) {
|
|
128
|
+
const replacements = Array.isArray(value) ? value : [value];
|
|
129
|
+
const targets = replacements.filter((rep) => typeof rep === "string" && rep.trim().length > 0).map(
|
|
130
|
+
(rep) => path2.isAbsolute(rep) ? rep : path2.resolve(baseDir, rep)
|
|
131
|
+
);
|
|
132
|
+
if (!targets.length) continue;
|
|
133
|
+
entries.push(createAliasEntry(pattern, targets));
|
|
134
|
+
}
|
|
135
|
+
return entries;
|
|
136
|
+
}
|
|
137
|
+
function configureResolverAliases(aliases, baseDir) {
|
|
138
|
+
customAliasEntries = aliases ? buildAliasEntries(aliases, baseDir) : [];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/cli/utils/config.ts
|
|
142
|
+
var CONFIG_BASENAMES = [
|
|
143
|
+
"ionify.config.ts",
|
|
144
|
+
"ionify.config.mts",
|
|
145
|
+
"ionify.config.js",
|
|
146
|
+
"ionify.config.mjs",
|
|
147
|
+
"ionify.config.cjs"
|
|
148
|
+
];
|
|
149
|
+
var cachedConfig = null;
|
|
150
|
+
var configLoaded = false;
|
|
151
|
+
async function bundleConfig(entry) {
|
|
152
|
+
const absDir = path3.dirname(entry);
|
|
153
|
+
const inlineIonifyPlugin = {
|
|
154
|
+
name: "inline-ionify",
|
|
155
|
+
setup(build2) {
|
|
156
|
+
build2.onResolve({ filter: /^ionify$/ }, () => ({
|
|
157
|
+
path: "ionify-virtual",
|
|
158
|
+
namespace: "ionify-ns"
|
|
159
|
+
}));
|
|
160
|
+
build2.onLoad({ filter: /.*/, namespace: "ionify-ns" }, () => ({
|
|
161
|
+
contents: `
|
|
162
|
+
export function defineConfig(config) {
|
|
163
|
+
return typeof config === 'function' ? config : () => config;
|
|
164
|
+
}
|
|
165
|
+
`,
|
|
166
|
+
loader: "js"
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const result = await build({
|
|
171
|
+
entryPoints: [entry],
|
|
172
|
+
bundle: true,
|
|
173
|
+
platform: "node",
|
|
174
|
+
format: "esm",
|
|
175
|
+
sourcemap: "inline",
|
|
176
|
+
write: false,
|
|
177
|
+
target: "node18",
|
|
178
|
+
logLevel: "silent",
|
|
179
|
+
absWorkingDir: absDir,
|
|
180
|
+
plugins: [inlineIonifyPlugin]
|
|
181
|
+
});
|
|
182
|
+
const output = result.outputFiles?.[0];
|
|
183
|
+
if (!output) throw new Error("Failed to bundle ionify config");
|
|
184
|
+
const dirnameLiteral = JSON.stringify(absDir);
|
|
185
|
+
const filenameLiteral = JSON.stringify(entry);
|
|
186
|
+
const importMetaLiteral = JSON.stringify(pathToFileURL(entry).href);
|
|
187
|
+
let contents = output.text;
|
|
188
|
+
if (contents.includes("import.meta.url")) {
|
|
189
|
+
contents = contents.replace(/import\.meta\.url/g, "__IONIFY_IMPORT_META_URL");
|
|
190
|
+
contents = `const __IONIFY_IMPORT_META_URL = ${importMetaLiteral};
|
|
191
|
+
${contents}`;
|
|
192
|
+
}
|
|
193
|
+
const preamble = `const __dirname = ${dirnameLiteral};
|
|
194
|
+
const __filename = ${filenameLiteral};
|
|
195
|
+
`;
|
|
196
|
+
return preamble + contents;
|
|
197
|
+
}
|
|
198
|
+
function findConfigFile(cwd) {
|
|
199
|
+
for (const name of CONFIG_BASENAMES) {
|
|
200
|
+
const candidate = path3.resolve(cwd, name);
|
|
201
|
+
if (fs2.existsSync(candidate) && fs2.statSync(candidate).isFile()) {
|
|
202
|
+
return candidate;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
async function loadIonifyConfig(cwd = process.cwd()) {
|
|
208
|
+
if (configLoaded) return cachedConfig;
|
|
209
|
+
configLoaded = true;
|
|
210
|
+
const configPath = findConfigFile(cwd);
|
|
211
|
+
if (!configPath) {
|
|
212
|
+
cachedConfig = null;
|
|
213
|
+
configureResolverAliases(void 0, cwd);
|
|
214
|
+
return cachedConfig;
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
const bundled = await bundleConfig(configPath);
|
|
218
|
+
const dataUrl = `data:text/javascript;base64,${Buffer.from(bundled).toString("base64")}`;
|
|
219
|
+
const imported = await import(dataUrl);
|
|
220
|
+
let resolved = imported?.default ?? imported?.config ?? imported ?? null;
|
|
221
|
+
if (resolved && typeof resolved === "function") {
|
|
222
|
+
resolved = resolved({ mode: process.env.NODE_ENV || "development" });
|
|
223
|
+
}
|
|
224
|
+
if (resolved && typeof resolved?.then === "function") {
|
|
225
|
+
resolved = await resolved;
|
|
226
|
+
}
|
|
227
|
+
if (resolved && typeof resolved === "object") {
|
|
228
|
+
cachedConfig = resolved;
|
|
229
|
+
const baseDir = path3.dirname(configPath);
|
|
230
|
+
const aliases = resolved?.resolve?.alias;
|
|
231
|
+
if (aliases && typeof aliases === "object") {
|
|
232
|
+
configureResolverAliases(aliases, baseDir);
|
|
233
|
+
} else {
|
|
234
|
+
configureResolverAliases(void 0, baseDir);
|
|
235
|
+
}
|
|
236
|
+
logInfo(`Loaded ionify config from ${path3.relative(cwd, configPath)}`);
|
|
237
|
+
} else {
|
|
238
|
+
throw new Error("Config did not export an object");
|
|
239
|
+
}
|
|
240
|
+
} catch (err) {
|
|
241
|
+
logError("Failed to load ionify.config", err);
|
|
242
|
+
cachedConfig = null;
|
|
243
|
+
configureResolverAliases(void 0, cwd);
|
|
244
|
+
}
|
|
245
|
+
return cachedConfig;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/cli/commands/dev.ts
|
|
249
|
+
async function startDevServer(options = {}) {
|
|
250
|
+
try {
|
|
251
|
+
const config = await loadIonifyConfig();
|
|
252
|
+
const port = parseInt(options.port || config?.server?.port || "5173");
|
|
253
|
+
logInfo(`Starting Ionify dev server on port ${port}...`);
|
|
254
|
+
const result = native.startDevServer({
|
|
255
|
+
port,
|
|
256
|
+
root: process.cwd(),
|
|
257
|
+
config: config || {}
|
|
258
|
+
});
|
|
259
|
+
logInfo(`Dev server running at http://localhost:${port}`);
|
|
260
|
+
return result;
|
|
261
|
+
} catch (err) {
|
|
262
|
+
logError(`Dev server failed: ${err.message}`);
|
|
263
|
+
throw err;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/cli/commands/build.ts
|
|
268
|
+
async function runBuildCommand(options = {}) {
|
|
269
|
+
try {
|
|
270
|
+
const config = await loadIonifyConfig();
|
|
271
|
+
const outDir = options.outDir || config?.build?.outDir || "dist";
|
|
272
|
+
const level = options.level ?? config?.optimizationLevel ?? 3;
|
|
273
|
+
logInfo(`Building for production (optimization level: ${level})...`);
|
|
274
|
+
const result = native.build({
|
|
275
|
+
root: process.cwd(),
|
|
276
|
+
outDir,
|
|
277
|
+
optimizationLevel: level,
|
|
278
|
+
config: config || {}
|
|
279
|
+
});
|
|
280
|
+
logInfo(`\u2705 Build complete! Output: ${outDir}/`);
|
|
281
|
+
return result;
|
|
282
|
+
} catch (err) {
|
|
283
|
+
logError(`Build failed: ${err.message}`);
|
|
284
|
+
throw err;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
73
288
|
// src/cli/commands/analyze.ts
|
|
289
|
+
import fs3 from "fs";
|
|
290
|
+
import path4 from "path";
|
|
74
291
|
function readGraphFromDisk(root) {
|
|
75
|
-
const file =
|
|
76
|
-
if (!
|
|
292
|
+
const file = path4.join(root, ".ionify", "graph.json");
|
|
293
|
+
if (!fs3.existsSync(file)) return null;
|
|
77
294
|
try {
|
|
78
|
-
const raw =
|
|
295
|
+
const raw = fs3.readFileSync(file, "utf8");
|
|
79
296
|
const snapshot = JSON.parse(raw);
|
|
80
297
|
if (snapshot?.version !== 1 || !snapshot?.nodes) return null;
|
|
81
298
|
return Object.entries(snapshot.nodes).map(([id, node]) => ({
|
|
@@ -173,13 +390,21 @@ async function runAnalyzeCommand(options = {}) {
|
|
|
173
390
|
// src/cli/index.ts
|
|
174
391
|
var program = new Command();
|
|
175
392
|
program.name("ionify").description("Ionify \u2013 Instant, Intelligent, Unified Build Engine").version("0.1.0");
|
|
176
|
-
program.command("dev").description("Start Ionify development server
|
|
177
|
-
|
|
178
|
-
|
|
393
|
+
program.command("dev").description("Start Ionify development server").option("-p, --port <port>", "Port to run the server on", "5173").action(async (options) => {
|
|
394
|
+
try {
|
|
395
|
+
await startDevServer(options);
|
|
396
|
+
} catch (err) {
|
|
397
|
+
logError(`Dev server failed: ${err.message}`);
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
179
400
|
});
|
|
180
|
-
program.command("build").description("Build for production
|
|
181
|
-
|
|
182
|
-
|
|
401
|
+
program.command("build").description("Build for production").option("-o, --outDir <dir>", "Output directory", "dist").option("-l, --level <level>", "Optimization level (0-4)", "3").action(async (options) => {
|
|
402
|
+
try {
|
|
403
|
+
await runBuildCommand(options);
|
|
404
|
+
} catch (err) {
|
|
405
|
+
logError(`Build failed: ${err.message}`);
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
183
408
|
});
|
|
184
409
|
program.command("analyze").description("Analyze bundle and performance").option("-f, --format <format>", "Output format (json|text)", "text").action(async (options) => {
|
|
185
410
|
try {
|