@mionjs/devtools 0.8.0-alpha.0 → 0.8.4
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/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.cjs +62 -26
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.cjs.map +1 -1
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.d.ts +11 -3
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.cjs +5 -4
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.cjs.map +1 -1
- package/build/vite-plugin/cjs/src/vite-plugin/constants.cjs +4 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.cjs.map +1 -1
- package/build/vite-plugin/cjs/src/vite-plugin/constants.d.ts +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.cjs +137 -30
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.cjs.map +1 -1
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.d.ts +2 -2
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.cjs +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.cjs.map +1 -1
- package/build/vite-plugin/cjs/src/vite-plugin/types.d.ts +6 -4
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.cjs +3 -2
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.cjs.map +1 -1
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.d.ts +11 -3
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.js +62 -26
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.js.map +1 -1
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.js +5 -4
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.js.map +1 -1
- package/build/vite-plugin/esm/src/vite-plugin/constants.d.ts +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.js +4 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.js.map +1 -1
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.d.ts +2 -2
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.js +138 -32
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.js.map +1 -1
- package/build/vite-plugin/esm/src/vite-plugin/transformers.js +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.js.map +1 -1
- package/build/vite-plugin/esm/src/vite-plugin/types.d.ts +6 -4
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.js +3 -2
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.js.map +1 -1
- package/package.json +2 -2
- package/src/vite-plugin/virtual-modules.d.ts +1 -1
|
@@ -5,13 +5,15 @@ export interface AOTCacheOptions {
|
|
|
5
5
|
cache?: boolean | string;
|
|
6
6
|
excludeReflection?: boolean;
|
|
7
7
|
customVirtualModuleId?: string;
|
|
8
|
+
isClient?: boolean;
|
|
8
9
|
}
|
|
9
10
|
export interface MionServerConfig {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
port?: number;
|
|
11
|
+
startScript: string;
|
|
12
|
+
viteConfig?: string;
|
|
13
|
+
runMode: 'buildOnly' | 'childProcess' | 'middleware';
|
|
14
14
|
waitTimeout?: number;
|
|
15
|
+
env?: Record<string, string>;
|
|
16
|
+
args?: string[];
|
|
15
17
|
}
|
|
16
18
|
export interface PureServerFnRegistryEntry {
|
|
17
19
|
readonly namespace: string;
|
|
@@ -29,13 +29,14 @@ function generateEntryCode(fn) {
|
|
|
29
29
|
return JSON.stringify(parts.length === 2 ? parts[1] : d);
|
|
30
30
|
});
|
|
31
31
|
const fnCode = fn.isFactory ? generateFactoryFn(fn) : generateDirectFn(fn);
|
|
32
|
+
const depsLine = depsArray.length > 0 ? `
|
|
33
|
+
pureFnDependencies: [${depsArray.join(", ")}],` : "";
|
|
32
34
|
return ` ${JSON.stringify(fn.fnName)}: {
|
|
33
35
|
namespace: ${JSON.stringify(fn.namespace)},
|
|
34
36
|
fnName: ${JSON.stringify(fn.fnName)},
|
|
35
37
|
paramNames: [${paramsArray}],
|
|
36
38
|
code: ${JSON.stringify(fn.fnBody)},
|
|
37
|
-
bodyHash: ${JSON.stringify(fn.bodyHash)}
|
|
38
|
-
pureFnDependencies: [${depsArray.join(", ")}],
|
|
39
|
+
bodyHash: ${JSON.stringify(fn.bodyHash)},${depsLine}
|
|
39
40
|
isFactory: ${fn.isFactory},
|
|
40
41
|
${fnCode}
|
|
41
42
|
}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"virtualModule.cjs","sources":["../../../../../src/vite-plugin/virtualModule.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {ExtractedPureFn} from './types.ts';\n\n/** Generates the virtual module source code from extracted server pure functions */\nexport function generateServerPureFnsVirtualModule(extractedFns: ExtractedPureFn[]): string {\n // Group functions by namespace\n const byNamespace = new Map<string, ExtractedPureFn[]>();\n for (const fn of extractedFns) {\n const list = byNamespace.get(fn.namespace) || [];\n list.push(fn);\n byNamespace.set(fn.namespace, list);\n }\n\n // Generate nested namespace entries\n const namespaceEntries: string[] = [];\n for (const [namespace, fns] of byNamespace) {\n const fnEntries = fns.map((fn) => generateEntryCode(fn));\n namespaceEntries.push(` ${JSON.stringify(namespace)}: {\\n${fnEntries.join(',\\n')}\\n }`);\n }\n\n return `// Auto-generated by @mionjs/server-pure-functions\n// Do not edit manually\n\nexport const serverPureFnsCache = {\n${namespaceEntries.join(',\\n')}\n};\n`;\n}\n\n/** Generates the code for a single server pure function entry */\nfunction generateEntryCode(fn: ExtractedPureFn): string {\n const paramsArray = fn.paramNames.map((p) => JSON.stringify(p)).join(', ');\n\n // Convert dependencies from \"namespace::fnName\" to just \"fnName\" (same-namespace resolution)\n const depsArray = [...fn.dependencies].map((d) => {\n const parts = d.split('::');\n return JSON.stringify(parts.length === 2 ? parts[1] : d);\n });\n\n // Generate the function code\n const fnCode = fn.isFactory ? generateFactoryFn(fn) : generateDirectFn(fn);\n\n return ` ${JSON.stringify(fn.fnName)}: {\n namespace: ${JSON.stringify(fn.namespace)},\n fnName: ${JSON.stringify(fn.fnName)},\n paramNames: [${paramsArray}],\n code: ${JSON.stringify(fn.fnBody)},\n bodyHash: ${JSON.stringify(fn.bodyHash)}
|
|
1
|
+
{"version":3,"file":"virtualModule.cjs","sources":["../../../../../src/vite-plugin/virtualModule.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {ExtractedPureFn} from './types.ts';\n\n/** Generates the virtual module source code from extracted server pure functions */\nexport function generateServerPureFnsVirtualModule(extractedFns: ExtractedPureFn[]): string {\n // Group functions by namespace\n const byNamespace = new Map<string, ExtractedPureFn[]>();\n for (const fn of extractedFns) {\n const list = byNamespace.get(fn.namespace) || [];\n list.push(fn);\n byNamespace.set(fn.namespace, list);\n }\n\n // Generate nested namespace entries\n const namespaceEntries: string[] = [];\n for (const [namespace, fns] of byNamespace) {\n const fnEntries = fns.map((fn) => generateEntryCode(fn));\n namespaceEntries.push(` ${JSON.stringify(namespace)}: {\\n${fnEntries.join(',\\n')}\\n }`);\n }\n\n return `// Auto-generated by @mionjs/server-pure-functions\n// Do not edit manually\n\nexport const serverPureFnsCache = {\n${namespaceEntries.join(',\\n')}\n};\n`;\n}\n\n/** Generates the code for a single server pure function entry */\nfunction generateEntryCode(fn: ExtractedPureFn): string {\n const paramsArray = fn.paramNames.map((p) => JSON.stringify(p)).join(', ');\n\n // Convert dependencies from \"namespace::fnName\" to just \"fnName\" (same-namespace resolution)\n const depsArray = [...fn.dependencies].map((d) => {\n const parts = d.split('::');\n return JSON.stringify(parts.length === 2 ? parts[1] : d);\n });\n\n // Generate the function code\n const fnCode = fn.isFactory ? generateFactoryFn(fn) : generateDirectFn(fn);\n\n const depsLine = depsArray.length > 0 ? `\\n pureFnDependencies: [${depsArray.join(', ')}],` : '';\n\n return ` ${JSON.stringify(fn.fnName)}: {\n namespace: ${JSON.stringify(fn.namespace)},\n fnName: ${JSON.stringify(fn.fnName)},\n paramNames: [${paramsArray}],\n code: ${JSON.stringify(fn.fnBody)},\n bodyHash: ${JSON.stringify(fn.bodyHash)},${depsLine}\n isFactory: ${fn.isFactory},\n${fnCode}\n }`;\n}\n\n/** Generates a direct fn property for a regular pure function */\nfunction generateDirectFn(fn: ExtractedPureFn): string {\n const params = fn.paramNames.join(', ');\n const safeName = makeSafeFunctionName(fn.fnName);\n\n return ` fn: function ${safeName}(${params}) {\n ${fn.fnBody}\n },`;\n}\n\n/** Generates createFn + fn: undefined for a factory function */\nfunction generateFactoryFn(fn: ExtractedPureFn): string {\n const params = fn.paramNames.join(', ');\n const safeName = makeSafeFunctionName(fn.fnName);\n\n return ` fn: undefined,\n createFn: function ${safeName}(jitUtils) {\n return (function factory(${params}) {\n ${fn.fnBody}\n })(jitUtils);\n },`;\n}\n\n/** Makes a function name safe for use as a JavaScript identifier */\nfunction makeSafeFunctionName(name: string): string {\n // Replace any non-alphanumeric characters with underscores\n const safe = name.replace(/[^a-zA-Z0-9_$]/g, '_');\n // Ensure it starts with a letter or underscore\n if (/^[0-9]/.test(safe)) {\n return '_' + safe;\n }\n return safe || 'anonymous';\n}\n"],"names":[],"mappings":";;AAUO,SAAS,mCAAmC,cAAyC;AAExF,QAAM,kCAAkB,IAAA;AACxB,aAAW,MAAM,cAAc;AAC3B,UAAM,OAAO,YAAY,IAAI,GAAG,SAAS,KAAK,CAAA;AAC9C,SAAK,KAAK,EAAE;AACZ,gBAAY,IAAI,GAAG,WAAW,IAAI;AAAA,EACtC;AAGA,QAAM,mBAA6B,CAAA;AACnC,aAAW,CAAC,WAAW,GAAG,KAAK,aAAa;AACxC,UAAM,YAAY,IAAI,IAAI,CAAC,OAAO,kBAAkB,EAAE,CAAC;AACvD,qBAAiB,KAAK,OAAO,KAAK,UAAU,SAAS,CAAC;AAAA,EAAQ,UAAU,KAAK,KAAK,CAAC;AAAA,MAAS;AAAA,EAChG;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA,EAIT,iBAAiB,KAAK,KAAK,CAAC;AAAA;AAAA;AAG9B;AAGA,SAAS,kBAAkB,IAA6B;AACpD,QAAM,cAAc,GAAG,WAAW,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI;AAGzE,QAAM,YAAY,CAAC,GAAG,GAAG,YAAY,EAAE,IAAI,CAAC,MAAM;AAC9C,UAAM,QAAQ,EAAE,MAAM,IAAI;AAC1B,WAAO,KAAK,UAAU,MAAM,WAAW,IAAI,MAAM,CAAC,IAAI,CAAC;AAAA,EAC3D,CAAC;AAGD,QAAM,SAAS,GAAG,YAAY,kBAAkB,EAAE,IAAI,iBAAiB,EAAE;AAEzE,QAAM,WAAW,UAAU,SAAS,IAAI;AAAA,mCAAsC,UAAU,KAAK,IAAI,CAAC,OAAO;AAEzG,SAAO,WAAW,KAAK,UAAU,GAAG,MAAM,CAAC;AAAA,yBACtB,KAAK,UAAU,GAAG,SAAS,CAAC;AAAA,sBAC/B,KAAK,UAAU,GAAG,MAAM,CAAC;AAAA,2BACpB,WAAW;AAAA,oBAClB,KAAK,UAAU,GAAG,MAAM,CAAC;AAAA,wBACrB,KAAK,UAAU,GAAG,QAAQ,CAAC,IAAI,QAAQ;AAAA,yBACtC,GAAG,SAAS;AAAA,EACnC,MAAM;AAAA;AAER;AAGA,SAAS,iBAAiB,IAA6B;AACnD,QAAM,SAAS,GAAG,WAAW,KAAK,IAAI;AACtC,QAAM,WAAW,qBAAqB,GAAG,MAAM;AAE/C,SAAO,4BAA4B,QAAQ,IAAI,MAAM;AAAA,kBACvC,GAAG,MAAM;AAAA;AAE3B;AAGA,SAAS,kBAAkB,IAA6B;AACpD,QAAM,SAAS,GAAG,WAAW,KAAK,IAAI;AACtC,QAAM,WAAW,qBAAqB,GAAG,MAAM;AAE/C,SAAO;AAAA,iCACsB,QAAQ;AAAA,2CACE,MAAM;AAAA,sBAC3B,GAAG,MAAM;AAAA;AAAA;AAG/B;AAGA,SAAS,qBAAqB,MAAsB;AAEhD,QAAM,OAAO,KAAK,QAAQ,mBAAmB,GAAG;AAEhD,MAAI,SAAS,KAAK,IAAI,GAAG;AACrB,WAAO,MAAM;AAAA,EACjB;AACA,SAAO,QAAQ;AACnB;;"}
|
|
@@ -8,10 +8,15 @@ export interface AOTCacheData {
|
|
|
8
8
|
export interface AOTCacheResult {
|
|
9
9
|
data: AOTCacheData;
|
|
10
10
|
childProcess?: ChildProcess;
|
|
11
|
+
platformReady?: Promise<PlatformReadyData>;
|
|
11
12
|
}
|
|
12
|
-
export
|
|
13
|
+
export interface PlatformReadyData {
|
|
14
|
+
routerConfig: Record<string, unknown>;
|
|
15
|
+
platformConfig: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export declare function generateAOTCaches(serverConfig: MionServerConfig, startScriptOverride?: string, isClient?: boolean): Promise<AOTCacheResult>;
|
|
13
18
|
export type ModuleLoader = (url: string) => Promise<Record<string, any>>;
|
|
14
|
-
export declare function loadSSRRouterAndGenerateAOTCaches(loadModule: ModuleLoader,
|
|
19
|
+
export declare function loadSSRRouterAndGenerateAOTCaches(loadModule: ModuleLoader, startScript: string, isClient?: boolean): Promise<AOTCacheData>;
|
|
15
20
|
export declare function killPersistentChild(child: ChildProcess | null): Promise<void>;
|
|
16
21
|
export declare function logAOTCaches(data: AOTCacheData): void;
|
|
17
22
|
export declare function generateJitFnsModule(jitFnsCode: string): string;
|
|
@@ -19,5 +24,8 @@ export declare function generatePureFnsModule(pureFnsCode: string): string;
|
|
|
19
24
|
export declare function generateRouterCacheModule(routerCacheCode: string): string;
|
|
20
25
|
export declare function generateCombinedCachesModule(): string;
|
|
21
26
|
export declare function generateNoopModule(comment: string): string;
|
|
22
|
-
export declare function
|
|
27
|
+
export declare function waitForPlatformReady(child: ChildProcess, timeoutMs?: number): Promise<{
|
|
28
|
+
routerConfig: Record<string, unknown>;
|
|
29
|
+
platformConfig: Record<string, unknown>;
|
|
30
|
+
}>;
|
|
23
31
|
export declare function generateNoopCombinedModule(): string;
|
|
@@ -2,11 +2,11 @@ import { fork } from "child_process";
|
|
|
2
2
|
import { resolve, dirname } from "path";
|
|
3
3
|
import { resolveModule } from "./resolveModule.js";
|
|
4
4
|
const DEFAULT_TIMEOUT = 3e4;
|
|
5
|
-
async function generateAOTCaches(serverConfig, startScriptOverride) {
|
|
6
|
-
const persist = serverConfig.
|
|
7
|
-
const startScript = resolve(startScriptOverride ?? serverConfig.
|
|
5
|
+
async function generateAOTCaches(serverConfig, startScriptOverride, isClient) {
|
|
6
|
+
const persist = serverConfig.runMode === "childProcess";
|
|
7
|
+
const startScript = resolve(startScriptOverride ?? serverConfig.startScript);
|
|
8
8
|
const scriptDir = dirname(startScript);
|
|
9
|
-
const viteConfigArgs = serverConfig.
|
|
9
|
+
const viteConfigArgs = serverConfig.viteConfig ? ["--config", resolve(serverConfig.viteConfig)] : [];
|
|
10
10
|
let viteNodePath;
|
|
11
11
|
try {
|
|
12
12
|
viteNodePath = await resolveModule("vite-node/cli", scriptDir);
|
|
@@ -22,8 +22,13 @@ Original error: ${err instanceof Error ? err.message : String(err)}`
|
|
|
22
22
|
let resolved = false;
|
|
23
23
|
let stderr = "";
|
|
24
24
|
try {
|
|
25
|
-
child = fork(viteNodePath, [...viteConfigArgs, startScript], {
|
|
26
|
-
env: {
|
|
25
|
+
child = fork(viteNodePath, [...viteConfigArgs, startScript, ...serverConfig.args || []], {
|
|
26
|
+
env: {
|
|
27
|
+
...process.env,
|
|
28
|
+
...serverConfig.env,
|
|
29
|
+
MION_COMPILE: serverConfig.runMode,
|
|
30
|
+
...isClient ? { MION_AOT_IS_CLIENT: "true" } : {}
|
|
31
|
+
},
|
|
27
32
|
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
28
33
|
cwd: scriptDir
|
|
29
34
|
});
|
|
@@ -39,9 +44,13 @@ Original error: ${err instanceof Error ? err.message : String(err)}`
|
|
|
39
44
|
}
|
|
40
45
|
const cleanup = () => {
|
|
41
46
|
clearTimeout(timeoutId);
|
|
42
|
-
if (child.connected) child.disconnect();
|
|
47
|
+
if (!persist && child.connected) child.disconnect();
|
|
43
48
|
if (!persist) child.kill();
|
|
44
49
|
};
|
|
50
|
+
let platformReadyResolve;
|
|
51
|
+
const platformReady = persist ? new Promise((res) => {
|
|
52
|
+
platformReadyResolve = res;
|
|
53
|
+
}) : void 0;
|
|
45
54
|
child.on("message", (msg) => {
|
|
46
55
|
const message = msg;
|
|
47
56
|
if (message?.type === "mion-aot-caches") {
|
|
@@ -53,8 +62,12 @@ Original error: ${err instanceof Error ? err.message : String(err)}`
|
|
|
53
62
|
pureFnsCode: message.pureFnsCode,
|
|
54
63
|
routerCacheCode: message.routerCacheCode
|
|
55
64
|
},
|
|
56
|
-
childProcess: persist ? child : void 0
|
|
65
|
+
childProcess: persist ? child : void 0,
|
|
66
|
+
platformReady
|
|
57
67
|
});
|
|
68
|
+
} else if (message?.type === "mion-platform-ready" && platformReadyResolve) {
|
|
69
|
+
platformReadyResolve({ routerConfig: message.routerConfig, platformConfig: message.platformConfig });
|
|
70
|
+
platformReadyResolve = void 0;
|
|
58
71
|
}
|
|
59
72
|
});
|
|
60
73
|
child.stderr?.on("data", (data) => {
|
|
@@ -82,7 +95,7 @@ Original error: ${err instanceof Error ? err.message : String(err)}`
|
|
|
82
95
|
reject(
|
|
83
96
|
new Error(
|
|
84
97
|
`vite-node exited with code ${code} before emitting AOT caches.
|
|
85
|
-
Make sure the
|
|
98
|
+
Make sure the startScript calls initMionRouter() and the router is fully initialized.
|
|
86
99
|
` + (stderr ? `stderr: ${stderr}` : "")
|
|
87
100
|
)
|
|
88
101
|
);
|
|
@@ -101,11 +114,13 @@ Make sure the startServerScript calls initMionRouter() and the router is fully i
|
|
|
101
114
|
}, DEFAULT_TIMEOUT);
|
|
102
115
|
});
|
|
103
116
|
}
|
|
104
|
-
async function loadSSRRouterAndGenerateAOTCaches(loadModule,
|
|
117
|
+
async function loadSSRRouterAndGenerateAOTCaches(loadModule, startScript, isClient) {
|
|
105
118
|
const prevCompile = process.env.MION_COMPILE;
|
|
106
|
-
process.env.
|
|
119
|
+
const prevIsClient = process.env.MION_AOT_IS_CLIENT;
|
|
120
|
+
process.env.MION_COMPILE = "middleware";
|
|
121
|
+
if (isClient) process.env.MION_AOT_IS_CLIENT = "true";
|
|
107
122
|
try {
|
|
108
|
-
const mod = await loadModule(
|
|
123
|
+
const mod = await loadModule(startScript);
|
|
109
124
|
const promises = Object.values(mod).filter((v) => v instanceof Promise);
|
|
110
125
|
if (promises.length > 0) await Promise.all(promises);
|
|
111
126
|
const aotModule = await loadModule("@mionjs/router/aot");
|
|
@@ -114,6 +129,8 @@ async function loadSSRRouterAndGenerateAOTCaches(loadModule, startServerScript)
|
|
|
114
129
|
} finally {
|
|
115
130
|
if (prevCompile === void 0) delete process.env.MION_COMPILE;
|
|
116
131
|
else process.env.MION_COMPILE = prevCompile;
|
|
132
|
+
if (prevIsClient === void 0) delete process.env.MION_AOT_IS_CLIENT;
|
|
133
|
+
else process.env.MION_AOT_IS_CLIENT = prevIsClient;
|
|
117
134
|
}
|
|
118
135
|
}
|
|
119
136
|
async function killPersistentChild(child) {
|
|
@@ -184,39 +201,58 @@ export const routerCache = ${routerCacheCode};
|
|
|
184
201
|
}
|
|
185
202
|
function generateCombinedCachesModule() {
|
|
186
203
|
return `/* Auto-generated combined AOT caches - do not edit */
|
|
187
|
-
import { addAOTCaches, addRoutesToCache } from '@mionjs/core';
|
|
188
204
|
import { pureFnsCache } from 'virtual:mion-aot/pure-fns';
|
|
189
205
|
import { jitFnsCache } from 'virtual:mion-aot/jit-fns';
|
|
190
206
|
import { routerCache } from 'virtual:mion-aot/router-cache';
|
|
207
|
+
import { addAOTCaches, addRoutesToCache } from '@mionjs/core';
|
|
191
208
|
|
|
209
|
+
// Auto-register AOT caches as a side effect so they survive tree-shaking
|
|
192
210
|
addAOTCaches(jitFnsCache, pureFnsCache);
|
|
193
211
|
addRoutesToCache(routerCache);
|
|
194
212
|
|
|
195
213
|
export { jitFnsCache, pureFnsCache, routerCache };
|
|
214
|
+
|
|
215
|
+
/** Loads pre-compiled AOT caches (no-op: caches are auto-registered on import). */
|
|
216
|
+
export function loadAOTCaches() {}
|
|
217
|
+
|
|
218
|
+
/** Returns the raw AOT caches. */
|
|
219
|
+
export function getRawAOTCaches() {
|
|
220
|
+
return { jitFnsCache, pureFnsCache, routerCache };
|
|
221
|
+
}
|
|
196
222
|
`;
|
|
197
223
|
}
|
|
198
224
|
function generateNoopModule(comment) {
|
|
199
225
|
return `/* ${comment} */
|
|
200
226
|
`;
|
|
201
227
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
228
|
+
function waitForPlatformReady(child, timeoutMs = 3e4) {
|
|
229
|
+
return new Promise((resolve2, reject) => {
|
|
230
|
+
const onMessage = (msg) => {
|
|
231
|
+
const message = msg;
|
|
232
|
+
if (message?.type === "mion-platform-ready") {
|
|
233
|
+
clearTimeout(timeoutId);
|
|
234
|
+
child.removeListener("message", onMessage);
|
|
235
|
+
resolve2({ routerConfig: message.routerConfig, platformConfig: message.platformConfig });
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
child.on("message", onMessage);
|
|
239
|
+
const timeoutId = setTimeout(() => {
|
|
240
|
+
child.removeListener("message", onMessage);
|
|
241
|
+
reject(
|
|
242
|
+
new Error(
|
|
243
|
+
`Server did not call setPlatformConfig() within ${timeoutMs / 1e3}s. Ensure your platform adapter (startNodeServer, startBunServer, etc.) is called after initMionRouter().`
|
|
244
|
+
)
|
|
245
|
+
);
|
|
246
|
+
}, timeoutMs);
|
|
247
|
+
});
|
|
214
248
|
}
|
|
215
249
|
function generateNoopCombinedModule() {
|
|
216
250
|
return `/* No-op: AOT caches not generated */
|
|
217
251
|
export const jitFnsCache = {};
|
|
218
252
|
export const pureFnsCache = {};
|
|
219
253
|
export const routerCache = {};
|
|
254
|
+
export function loadAOTCaches() {}
|
|
255
|
+
export function getRawAOTCaches() { return { jitFnsCache, pureFnsCache, routerCache }; }
|
|
220
256
|
`;
|
|
221
257
|
}
|
|
222
258
|
export {
|
|
@@ -230,6 +266,6 @@ export {
|
|
|
230
266
|
killPersistentChild,
|
|
231
267
|
loadSSRRouterAndGenerateAOTCaches,
|
|
232
268
|
logAOTCaches,
|
|
233
|
-
|
|
269
|
+
waitForPlatformReady
|
|
234
270
|
};
|
|
235
271
|
//# sourceMappingURL=aotCacheGenerator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aotCacheGenerator.js","sources":["../../../../../src/vite-plugin/aotCacheGenerator.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {fork, ChildProcess} from 'child_process';\nimport {resolve, dirname} from 'path';\nimport {MionServerConfig} from './types.ts';\nimport {resolveModule} from './resolveModule.ts';\n\n/** AOT cache data returned from the generator */\nexport interface AOTCacheData {\n jitFnsCode: string;\n pureFnsCode: string;\n routerCacheCode: string;\n}\n\n/** AOT cache result including optional persistent child process */\nexport interface AOTCacheResult {\n data: AOTCacheData;\n /** The child process, only present when server mode is 'IPC' (persist) */\n childProcess?: ChildProcess;\n}\n\n/** IPC message type from the router's aotEmitter */\ninterface AOTCacheMessage {\n type: 'mion-aot-caches';\n jitFnsCode: string;\n pureFnsCode: string;\n routerCacheCode: string;\n}\n\n/** Default timeout for AOT cache generation (30 seconds) */\nconst DEFAULT_TIMEOUT = 30000;\n\n/**\n * Generates AOT caches by spawning vite-node to run the server's start script.\n *\n * vite-node is used instead of plain node/ts-node because:\n * 1. The server needs deepkit type compiler transformations\n * 2. The server's vite.config.ts has aliases and plugins that must be applied\n * 3. vite-node provides the same environment as the actual server build\n *\n * The router's emitAOTCaches() (called automatically from initMionRouter)\n * detects MION_COMPILE and sends the serialized caches via IPC.\n */\nexport async function generateAOTCaches(serverConfig: MionServerConfig, startScriptOverride?: string): Promise<AOTCacheResult> {\n const persist = serverConfig.mode === 'IPC';\n const startScript = resolve(startScriptOverride ?? serverConfig.startServerScript);\n const scriptDir = dirname(startScript);\n\n // Determine the vite config to use\n // If serverViteConfig is provided, use it; otherwise let vite-node auto-discover\n const viteConfigArgs = serverConfig.serverViteConfig ? ['--config', resolve(serverConfig.serverViteConfig)] : [];\n\n // Resolve vite-node path in both CJS and ESM environments\n let viteNodePath: string;\n try {\n viteNodePath = await resolveModule('vite-node/cli', scriptDir);\n } catch (err) {\n throw new Error(\n `Failed to resolve vite-node. Make sure vite-node is installed.\\n` +\n `You can install it with: npm install -D vite-node\\n` +\n `Original error: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n return new Promise((resolvePromise, reject) => {\n let child: ChildProcess;\n let resolved = false;\n let stderr = '';\n\n try {\n // Spawn vite-node as a child process with IPC channel\n // 'serve' mode tells platform adapters to proceed with server.listen()\n child = fork(viteNodePath, [...viteConfigArgs, startScript], {\n env: {...process.env, MION_COMPILE: persist ? 'serve' : 'onlyAOT'},\n stdio: ['pipe', 'pipe', 'pipe', 'ipc'],\n cwd: scriptDir,\n });\n } catch (err) {\n // If vite-node is not found, provide a helpful error message\n reject(\n new Error(\n `Failed to spawn vite-node. Make sure vite-node is installed.\\n` +\n `You can install it with: npm install -D vite-node\\n` +\n `Original error: ${err instanceof Error ? err.message : String(err)}`\n )\n );\n return;\n }\n\n /** Cleanup child process: disconnect IPC, clear timeout, and optionally kill */\n const cleanup = () => {\n clearTimeout(timeoutId);\n if (child.connected) child.disconnect();\n if (!persist) child.kill();\n };\n\n child.on('message', (msg: unknown) => {\n const message = msg as AOTCacheMessage;\n if (message?.type === 'mion-aot-caches') {\n resolved = true;\n cleanup();\n resolvePromise({\n data: {\n jitFnsCode: message.jitFnsCode,\n pureFnsCode: message.pureFnsCode,\n routerCacheCode: message.routerCacheCode,\n },\n childProcess: persist ? child : undefined,\n });\n }\n });\n\n // Capture stderr for error reporting (and pipe through in persist mode)\n child.stderr?.on('data', (data: Buffer) => {\n const msg = data.toString().trim();\n if (!resolved) stderr += msg + '\\n';\n if (persist && msg) console.error(`[mion-server] ${msg}`);\n });\n\n // Pipe stdout through (always in persist mode, only with DEBUG_AOT otherwise)\n child.stdout?.on('data', (data: Buffer) => {\n const msg = data.toString().trim();\n if (persist && msg) {\n console.log(`[mion-server] ${msg}`);\n } else if (process.env.DEBUG_AOT && msg) {\n console.log('[mion-aot] stdout:', msg);\n }\n });\n\n child.on('error', (err) => {\n if (!resolved) {\n cleanup();\n reject(new Error(`vite-node failed to start: ${err.message}`));\n }\n });\n\n child.on('exit', (code) => {\n if (!resolved) {\n cleanup();\n reject(\n new Error(\n `vite-node exited with code ${code} before emitting AOT caches.\\n` +\n `Make sure the startServerScript calls initMionRouter() and the router ` +\n `is fully initialized.\\n` +\n (stderr ? `stderr: ${stderr}` : '')\n )\n );\n }\n });\n\n // Timeout safety\n const timeoutId = setTimeout(() => {\n if (!resolved) {\n cleanup();\n if (persist) child.kill();\n reject(\n new Error(\n `AOT cache generation timed out (${DEFAULT_TIMEOUT / 1000}s). ` +\n `Make sure the server start script completes initialization.`\n )\n );\n }\n }, DEFAULT_TIMEOUT);\n });\n}\n\n/** Loads a module by URL — abstracts over ssrLoadModule and Environment Runner */\nexport type ModuleLoader = (url: string) => Promise<Record<string, any>>;\n\n/**\n * Loads the startServerScript with MION_COMPILE=SSR so platform adapters skip server.listen().\n * Generates AOT caches via SSR using a module loader (for same-Vite-process scenarios like Nuxt).\n * After loading, retrieves the caches directly from the router's global state via getSerializedCaches(). */\nexport async function loadSSRRouterAndGenerateAOTCaches(\n loadModule: ModuleLoader,\n startServerScript: string\n): Promise<AOTCacheData> {\n const prevCompile = process.env.MION_COMPILE;\n process.env.MION_COMPILE = 'viteSSR';\n\n try {\n // Load the start server script — triggers initMionRouter(), populates caches,\n // skips process.send (SSR mode) and skips server.listen() (platform adapters)\n const mod = await loadModule(startServerScript);\n // Await any Promise-valued exports (e.g. initMionRouter() without top-level await)\n const promises = Object.values(mod).filter((v): v is Promise<any> => v instanceof Promise);\n if (promises.length > 0) await Promise.all(promises);\n\n // Get caches directly from the router's global state\n const aotModule = await loadModule('@mionjs/router/aot');\n const caches: AOTCacheData = await aotModule.getSerializedCaches();\n return caches;\n } finally {\n if (prevCompile === undefined) delete process.env.MION_COMPILE;\n else process.env.MION_COMPILE = prevCompile;\n }\n}\n\n// ============ Persistent child management ============\n\n/** Kills a persistent child process gracefully (SIGTERM then SIGKILL after 5s) */\nexport async function killPersistentChild(child: ChildProcess | null): Promise<void> {\n if (!child || child.killed) return;\n const pid = child.pid;\n\n if (pid) {\n try {\n process.kill(-pid, 'SIGTERM');\n } catch {\n child.kill('SIGTERM');\n }\n } else {\n child.kill('SIGTERM');\n }\n\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n if (child && !child.killed) {\n if (pid) {\n try {\n process.kill(-pid, 'SIGKILL');\n } catch {\n child.kill('SIGKILL');\n }\n } else {\n child.kill('SIGKILL');\n }\n }\n resolve();\n }, 5000);\n\n child!.on('exit', () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n}\n\n// ============ Logging ============\n\n/** Formats byte size to human-readable string */\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n const kb = bytes / 1024;\n if (kb < 1024) return `${kb.toFixed(1)} KB`;\n const mb = kb / 1024;\n return `${mb.toFixed(1)} MB`;\n}\n\n/** Logs AOT cache sizes and optionally full contents in verbose mode (DEBUG_AOT) */\nexport function logAOTCaches(data: AOTCacheData): void {\n const jitSize = formatBytes(Buffer.byteLength(data.jitFnsCode, 'utf-8'));\n const pureSize = formatBytes(Buffer.byteLength(data.pureFnsCode, 'utf-8'));\n const routerSize = formatBytes(Buffer.byteLength(data.routerCacheCode, 'utf-8'));\n console.log(`[mion] jitFns: ${jitSize}, pureFns: ${pureSize}, routerCache: ${routerSize}`);\n\n if (process.env.DEBUG_AOT) {\n console.log('[mion] AOT jitFns cache:\\n', data.jitFnsCode);\n console.log('[mion] AOT pureFns cache:\\n', data.pureFnsCode);\n console.log('[mion] AOT routerCache:\\n', data.routerCacheCode);\n }\n}\n\n// ============ Module Generators ============\n\n/** Generates the virtual module code for JIT functions cache (exports only). */\nexport function generateJitFnsModule(jitFnsCode: string): string {\n return `/* Auto-generated AOT JIT functions cache - do not edit */\nexport const jitFnsCache = ${jitFnsCode};\n`;\n}\n\n/** Generates the virtual module code for pure functions cache (exports only). */\nexport function generatePureFnsModule(pureFnsCode: string): string {\n return `/* Auto-generated AOT pure functions cache - do not edit */\nexport const pureFnsCache = ${pureFnsCode};\n`;\n}\n\n/** Generates the virtual module code for router methods cache (exports only). */\nexport function generateRouterCacheModule(routerCacheCode: string): string {\n return `/* Auto-generated AOT router cache - do not edit */\nexport const routerCache = ${routerCacheCode};\n`;\n}\n\n/** Generates the combined virtual module that imports all 3 caches, registers them, and re-exports. */\nexport function generateCombinedCachesModule(): string {\n return `/* Auto-generated combined AOT caches - do not edit */\nimport { addAOTCaches, addRoutesToCache } from '@mionjs/core';\nimport { pureFnsCache } from 'virtual:mion-aot/pure-fns';\nimport { jitFnsCache } from 'virtual:mion-aot/jit-fns';\nimport { routerCache } from 'virtual:mion-aot/router-cache';\n\naddAOTCaches(jitFnsCache, pureFnsCache);\naddRoutesToCache(routerCache);\n\nexport { jitFnsCache, pureFnsCache, routerCache };\n`;\n}\n\n/** Generates a no-op module for when AOT caches are disabled. */\nexport function generateNoopModule(comment: string): string {\n return `/* ${comment} */\\n`;\n}\n\n/** Poll an HTTP port until the server responds (2xx or 404). */\nexport async function waitForServer(port: number, timeoutMs = 30000): Promise<void> {\n const startTime = Date.now();\n const checkInterval = 100;\n while (Date.now() - startTime < timeoutMs) {\n try {\n const response = await fetch(`http://localhost:${port}/`);\n if (response.ok || response.status === 404) return;\n } catch {\n // Server not ready yet\n }\n await new Promise((r) => setTimeout(r, checkInterval));\n }\n throw new Error(`[mion] Server failed to become ready on port ${port} within ${timeoutMs}ms`);\n}\n\n/** Generates a no-op combined module that exports empty caches. */\nexport function generateNoopCombinedModule(): string {\n return `/* No-op: AOT caches not generated */\nexport const jitFnsCache = {};\nexport const pureFnsCache = {};\nexport const routerCache = {};\n`;\n}\n"],"names":["resolve"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"aotCacheGenerator.js","sources":["../../../../../src/vite-plugin/aotCacheGenerator.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {fork, ChildProcess} from 'child_process';\nimport {resolve, dirname} from 'path';\nimport {MionServerConfig} from './types.ts';\nimport {resolveModule} from './resolveModule.ts';\n\n/** AOT cache data returned from the generator */\nexport interface AOTCacheData {\n jitFnsCode: string;\n pureFnsCode: string;\n routerCacheCode: string;\n}\n\n/** AOT cache result including optional persistent child process */\nexport interface AOTCacheResult {\n data: AOTCacheData;\n /** The child process, only present when runMode is 'childProcess' (persist) */\n childProcess?: ChildProcess;\n /** Promise that resolves when the server calls setPlatformConfig() (childProcess mode only) */\n platformReady?: Promise<PlatformReadyData>;\n}\n\n/** IPC message type from the router's aotEmitter */\ninterface AOTCacheMessage {\n type: 'mion-aot-caches';\n jitFnsCode: string;\n pureFnsCode: string;\n routerCacheCode: string;\n}\n\n/** Platform config received from the server via IPC */\nexport interface PlatformReadyData {\n routerConfig: Record<string, unknown>;\n platformConfig: Record<string, unknown>;\n}\n\ninterface PlatformReadyMessage extends PlatformReadyData {\n type: 'mion-platform-ready';\n}\n\n/** Default timeout for AOT cache generation (30 seconds) */\nconst DEFAULT_TIMEOUT = 30000;\n\n/**\n * Generates AOT caches by spawning vite-node to run the server's start script.\n *\n * vite-node is used instead of plain node/ts-node because:\n * 1. The server needs deepkit type compiler transformations\n * 2. The server's vite.config.ts has aliases and plugins that must be applied\n * 3. vite-node provides the same environment as the actual server build\n *\n * The router's emitAOTCaches() (called automatically from initMionRouter)\n * detects MION_COMPILE and sends the serialized caches via IPC.\n */\nexport async function generateAOTCaches(\n serverConfig: MionServerConfig,\n startScriptOverride?: string,\n isClient?: boolean\n): Promise<AOTCacheResult> {\n const persist = serverConfig.runMode === 'childProcess';\n const startScript = resolve(startScriptOverride ?? serverConfig.startScript);\n const scriptDir = dirname(startScript);\n\n // Determine the vite config to use\n // If viteConfig is provided, use it; otherwise let vite-node auto-discover\n const viteConfigArgs = serverConfig.viteConfig ? ['--config', resolve(serverConfig.viteConfig)] : [];\n\n // Resolve vite-node path in both CJS and ESM environments\n let viteNodePath: string;\n try {\n viteNodePath = await resolveModule('vite-node/cli', scriptDir);\n } catch (err) {\n throw new Error(\n `Failed to resolve vite-node. Make sure vite-node is installed.\\n` +\n `You can install it with: npm install -D vite-node\\n` +\n `Original error: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n return new Promise((resolvePromise, reject) => {\n let child: ChildProcess;\n let resolved = false;\n let stderr = '';\n\n try {\n // Spawn vite-node as a child process with IPC channel\n // 'serve' mode tells platform adapters to proceed with server.listen()\n child = fork(viteNodePath, [...viteConfigArgs, startScript, ...(serverConfig.args || [])], {\n env: {\n ...process.env,\n ...serverConfig.env,\n MION_COMPILE: serverConfig.runMode,\n ...(isClient ? {MION_AOT_IS_CLIENT: 'true'} : {}),\n },\n stdio: ['pipe', 'pipe', 'pipe', 'ipc'],\n cwd: scriptDir,\n });\n } catch (err) {\n // If vite-node is not found, provide a helpful error message\n reject(\n new Error(\n `Failed to spawn vite-node. Make sure vite-node is installed.\\n` +\n `You can install it with: npm install -D vite-node\\n` +\n `Original error: ${err instanceof Error ? err.message : String(err)}`\n )\n );\n return;\n }\n\n /** Cleanup child process: clear timeout, disconnect IPC (only in non-persist mode), and optionally kill.\n * In persist mode, IPC stays open for the mion-platform-ready message. */\n const cleanup = () => {\n clearTimeout(timeoutId);\n if (!persist && child.connected) child.disconnect();\n if (!persist) child.kill();\n };\n\n // Set up platform-ready listener BEFORE any messages arrive to avoid race conditions.\n // The child may send mion-platform-ready very quickly after mion-aot-caches.\n let platformReadyResolve: ((value: PlatformReadyData) => void) | undefined;\n const platformReady = persist\n ? new Promise<PlatformReadyData>((res) => {\n platformReadyResolve = res;\n })\n : undefined;\n\n child.on('message', (msg: unknown) => {\n const message = msg as AOTCacheMessage | PlatformReadyMessage;\n if (message?.type === 'mion-aot-caches') {\n resolved = true;\n cleanup();\n resolvePromise({\n data: {\n jitFnsCode: message.jitFnsCode,\n pureFnsCode: message.pureFnsCode,\n routerCacheCode: message.routerCacheCode,\n },\n childProcess: persist ? child : undefined,\n platformReady,\n });\n } else if (message?.type === 'mion-platform-ready' && platformReadyResolve) {\n platformReadyResolve({routerConfig: message.routerConfig, platformConfig: message.platformConfig});\n platformReadyResolve = undefined;\n }\n });\n\n // Capture stderr for error reporting (and pipe through in persist mode)\n child.stderr?.on('data', (data: Buffer) => {\n const msg = data.toString().trim();\n if (!resolved) stderr += msg + '\\n';\n if (persist && msg) console.error(`[mion-server] ${msg}`);\n });\n\n // Pipe stdout through (always in persist mode, only with DEBUG_AOT otherwise)\n child.stdout?.on('data', (data: Buffer) => {\n const msg = data.toString().trim();\n if (persist && msg) {\n console.log(`[mion-server] ${msg}`);\n } else if (process.env.DEBUG_AOT && msg) {\n console.log('[mion-aot] stdout:', msg);\n }\n });\n\n child.on('error', (err) => {\n if (!resolved) {\n cleanup();\n reject(new Error(`vite-node failed to start: ${err.message}`));\n }\n });\n\n child.on('exit', (code) => {\n if (!resolved) {\n cleanup();\n reject(\n new Error(\n `vite-node exited with code ${code} before emitting AOT caches.\\n` +\n `Make sure the startScript calls initMionRouter() and the router ` +\n `is fully initialized.\\n` +\n (stderr ? `stderr: ${stderr}` : '')\n )\n );\n }\n });\n\n // Timeout safety\n const timeoutId = setTimeout(() => {\n if (!resolved) {\n cleanup();\n if (persist) child.kill();\n reject(\n new Error(\n `AOT cache generation timed out (${DEFAULT_TIMEOUT / 1000}s). ` +\n `Make sure the server start script completes initialization.`\n )\n );\n }\n }, DEFAULT_TIMEOUT);\n });\n}\n\n/** Loads a module by URL — abstracts over ssrLoadModule and Environment Runner */\nexport type ModuleLoader = (url: string) => Promise<Record<string, any>>;\n\n/**\n * Loads the startScript with MION_COMPILE=middleware so platform adapters skip server.listen().\n * Generates AOT caches via SSR using a module loader (for same-Vite-process scenarios like Nuxt).\n * After loading, retrieves the caches directly from the router's global state via getSerializedCaches(). */\nexport async function loadSSRRouterAndGenerateAOTCaches(\n loadModule: ModuleLoader,\n startScript: string,\n isClient?: boolean\n): Promise<AOTCacheData> {\n const prevCompile = process.env.MION_COMPILE;\n const prevIsClient = process.env.MION_AOT_IS_CLIENT;\n process.env.MION_COMPILE = 'middleware';\n if (isClient) process.env.MION_AOT_IS_CLIENT = 'true';\n\n try {\n // Load the start server script — triggers initMionRouter(), populates caches,\n // skips process.send (SSR mode) and skips server.listen() (platform adapters)\n const mod = await loadModule(startScript);\n // Await any Promise-valued exports (e.g. initMionRouter() without top-level await)\n const promises = Object.values(mod).filter((v): v is Promise<any> => v instanceof Promise);\n if (promises.length > 0) await Promise.all(promises);\n\n // Get caches directly from the router's global state\n const aotModule = await loadModule('@mionjs/router/aot');\n const caches: AOTCacheData = await aotModule.getSerializedCaches();\n return caches;\n } finally {\n if (prevCompile === undefined) delete process.env.MION_COMPILE;\n else process.env.MION_COMPILE = prevCompile;\n if (prevIsClient === undefined) delete process.env.MION_AOT_IS_CLIENT;\n else process.env.MION_AOT_IS_CLIENT = prevIsClient;\n }\n}\n\n// ============ Persistent child management ============\n\n/** Kills a persistent child process gracefully (SIGTERM then SIGKILL after 5s) */\nexport async function killPersistentChild(child: ChildProcess | null): Promise<void> {\n if (!child || child.killed) return;\n const pid = child.pid;\n\n if (pid) {\n try {\n process.kill(-pid, 'SIGTERM');\n } catch {\n child.kill('SIGTERM');\n }\n } else {\n child.kill('SIGTERM');\n }\n\n await new Promise<void>((resolve) => {\n const timeout = setTimeout(() => {\n if (child && !child.killed) {\n if (pid) {\n try {\n process.kill(-pid, 'SIGKILL');\n } catch {\n child.kill('SIGKILL');\n }\n } else {\n child.kill('SIGKILL');\n }\n }\n resolve();\n }, 5000);\n\n child!.on('exit', () => {\n clearTimeout(timeout);\n resolve();\n });\n });\n}\n\n// ============ Logging ============\n\n/** Formats byte size to human-readable string */\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n const kb = bytes / 1024;\n if (kb < 1024) return `${kb.toFixed(1)} KB`;\n const mb = kb / 1024;\n return `${mb.toFixed(1)} MB`;\n}\n\n/** Logs AOT cache sizes and optionally full contents in verbose mode (DEBUG_AOT) */\nexport function logAOTCaches(data: AOTCacheData): void {\n const jitSize = formatBytes(Buffer.byteLength(data.jitFnsCode, 'utf-8'));\n const pureSize = formatBytes(Buffer.byteLength(data.pureFnsCode, 'utf-8'));\n const routerSize = formatBytes(Buffer.byteLength(data.routerCacheCode, 'utf-8'));\n console.log(`[mion] jitFns: ${jitSize}, pureFns: ${pureSize}, routerCache: ${routerSize}`);\n\n if (process.env.DEBUG_AOT) {\n console.log('[mion] AOT jitFns cache:\\n', data.jitFnsCode);\n console.log('[mion] AOT pureFns cache:\\n', data.pureFnsCode);\n console.log('[mion] AOT routerCache:\\n', data.routerCacheCode);\n }\n}\n\n// ============ Module Generators ============\n\n/** Generates the virtual module code for JIT functions cache (exports only). */\nexport function generateJitFnsModule(jitFnsCode: string): string {\n return `/* Auto-generated AOT JIT functions cache - do not edit */\nexport const jitFnsCache = ${jitFnsCode};\n`;\n}\n\n/** Generates the virtual module code for pure functions cache (exports only). */\nexport function generatePureFnsModule(pureFnsCode: string): string {\n return `/* Auto-generated AOT pure functions cache - do not edit */\nexport const pureFnsCache = ${pureFnsCode};\n`;\n}\n\n/** Generates the virtual module code for router methods cache (exports only). */\nexport function generateRouterCacheModule(routerCacheCode: string): string {\n return `/* Auto-generated AOT router cache - do not edit */\nexport const routerCache = ${routerCacheCode};\n`;\n}\n\n/** Generates the combined virtual module that imports all 3 caches and re-exports them. */\nexport function generateCombinedCachesModule(): string {\n return `/* Auto-generated combined AOT caches - do not edit */\nimport { pureFnsCache } from 'virtual:mion-aot/pure-fns';\nimport { jitFnsCache } from 'virtual:mion-aot/jit-fns';\nimport { routerCache } from 'virtual:mion-aot/router-cache';\nimport { addAOTCaches, addRoutesToCache } from '@mionjs/core';\n\n// Auto-register AOT caches as a side effect so they survive tree-shaking\naddAOTCaches(jitFnsCache, pureFnsCache);\naddRoutesToCache(routerCache);\n\nexport { jitFnsCache, pureFnsCache, routerCache };\n\n/** Loads pre-compiled AOT caches (no-op: caches are auto-registered on import). */\nexport function loadAOTCaches() {}\n\n/** Returns the raw AOT caches. */\nexport function getRawAOTCaches() {\n return { jitFnsCache, pureFnsCache, routerCache };\n}\n`;\n}\n\n/** Generates a no-op module for when AOT caches are disabled. */\nexport function generateNoopModule(comment: string): string {\n return `/* ${comment} */\\n`;\n}\n\n/** Waits for the server child process to send a mion-platform-ready IPC message. */\nexport function waitForPlatformReady(\n child: ChildProcess,\n timeoutMs = 30000\n): Promise<{routerConfig: Record<string, unknown>; platformConfig: Record<string, unknown>}> {\n return new Promise((resolve, reject) => {\n const onMessage = (msg: unknown) => {\n const message = msg as PlatformReadyMessage;\n if (message?.type === 'mion-platform-ready') {\n clearTimeout(timeoutId);\n child.removeListener('message', onMessage);\n resolve({routerConfig: message.routerConfig, platformConfig: message.platformConfig});\n }\n };\n child.on('message', onMessage);\n const timeoutId = setTimeout(() => {\n child.removeListener('message', onMessage);\n reject(\n new Error(\n `Server did not call setPlatformConfig() within ${timeoutMs / 1000}s. ` +\n `Ensure your platform adapter (startNodeServer, startBunServer, etc.) is called after initMionRouter().`\n )\n );\n }, timeoutMs);\n });\n}\n\n/** Generates a no-op combined module that exports empty caches. */\nexport function generateNoopCombinedModule(): string {\n return `/* No-op: AOT caches not generated */\nexport const jitFnsCache = {};\nexport const pureFnsCache = {};\nexport const routerCache = {};\nexport function loadAOTCaches() {}\nexport function getRawAOTCaches() { return { jitFnsCache, pureFnsCache, routerCache }; }\n`;\n}\n"],"names":["resolve"],"mappings":"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"}
|
|
@@ -46,7 +46,7 @@ function collectFileStats(dir, baseDir) {
|
|
|
46
46
|
return entries;
|
|
47
47
|
}
|
|
48
48
|
function computeSourceHash(serverConfig, aotOptions) {
|
|
49
|
-
const serverDir = dirname(resolve(serverConfig.
|
|
49
|
+
const serverDir = dirname(resolve(serverConfig.startScript));
|
|
50
50
|
const fileStats = collectFileStats(serverDir, serverDir);
|
|
51
51
|
fileStats.sort();
|
|
52
52
|
const hashInput = [
|
|
@@ -54,7 +54,8 @@ function computeSourceHash(serverConfig, aotOptions) {
|
|
|
54
54
|
`cacheVersion:${AOT_DISK_CACHE_VERSION}`,
|
|
55
55
|
`devtoolsVersion:${getDevtoolsVersion()}`,
|
|
56
56
|
`excludedFns:${JSON.stringify((aotOptions?.excludedFns || []).slice().sort())}`,
|
|
57
|
-
`excludedPureFns:${JSON.stringify((aotOptions?.excludedPureFns || []).slice().sort())}
|
|
57
|
+
`excludedPureFns:${JSON.stringify((aotOptions?.excludedPureFns || []).slice().sort())}`,
|
|
58
|
+
`isClient:${!!aotOptions?.isClient}`
|
|
58
59
|
].join("\n");
|
|
59
60
|
return createHash("sha256").update(hashInput).digest("hex");
|
|
60
61
|
}
|
|
@@ -94,7 +95,7 @@ function resolveCacheDir(options, viteCacheDir) {
|
|
|
94
95
|
}
|
|
95
96
|
async function getOrGenerateAOTCaches(serverConfig, aotOptions, cacheDir) {
|
|
96
97
|
const forceRegenerate = process.env.MION_AOT_FORCE === "true";
|
|
97
|
-
const isIPCMode = serverConfig.
|
|
98
|
+
const isIPCMode = serverConfig.runMode === "childProcess";
|
|
98
99
|
const cachingEnabled = cacheDir !== "" && !forceRegenerate && !isIPCMode;
|
|
99
100
|
let hash = "";
|
|
100
101
|
if (cachingEnabled) {
|
|
@@ -111,7 +112,7 @@ async function getOrGenerateAOTCaches(serverConfig, aotOptions, cacheDir) {
|
|
|
111
112
|
};
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
|
-
const result = await generateAOTCaches(serverConfig);
|
|
115
|
+
const result = await generateAOTCaches(serverConfig, void 0, aotOptions?.isClient);
|
|
115
116
|
if (cachingEnabled) {
|
|
116
117
|
if (!hash) hash = computeSourceHash(serverConfig, aotOptions);
|
|
117
118
|
writeDiskCache(cacheDir, hash, result.data);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aotDiskCache.js","sources":["../../../../../src/vite-plugin/aotDiskCache.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {createHash} from 'crypto';\nimport {readFileSync, writeFileSync, mkdirSync, readdirSync, statSync} from 'fs';\nimport {join, resolve, dirname, relative} from 'path';\nimport {AOTCacheOptions, MionServerConfig} from './types.ts';\nimport {generateAOTCaches, AOTCacheData, AOTCacheResult} from './aotCacheGenerator.ts';\n\n/** Schema for the on-disk cache file */\ninterface AOTDiskCacheFile {\n cacheVersion: string;\n hash: string;\n jitFnsCode: string;\n pureFnsCode: string;\n routerCacheCode: string;\n}\n\n/** Bump when mion internals change in a way that invalidates cached output */\nconst AOT_DISK_CACHE_VERSION = '2';\n\n/** Cache file name */\nconst CACHE_FILENAME = 'mion-aot-cache.json';\n\n/** Directories to skip when walking the server source tree */\nconst SKIP_DIRS = new Set(['node_modules', '.dist', 'dist', '.git', '.vite', 'build', 'coverage', '.coverage']);\n\n/** File extensions to include in hash computation */\nconst SOURCE_EXTENSIONS = /\\.(ts|tsx)$/;\n\n/** Test file patterns to exclude from hash computation */\nconst TEST_FILE_PATTERN = /\\.(spec|test)\\.(ts|tsx)$/;\n\n/** Read the devtools package version (cached after first call) */\nlet devtoolsVersion: string | null = null;\nfunction getDevtoolsVersion(): string {\n if (devtoolsVersion) return devtoolsVersion;\n try {\n const pkgPath = resolve(dirname(new URL(import.meta.url).pathname), '../../package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n devtoolsVersion = pkg.version || '0.0.0';\n } catch {\n devtoolsVersion = '0.0.0';\n }\n return devtoolsVersion as string;\n}\n\n/** Recursively collect file stats (relativePath:mtimeMs:size) for source files */\nfunction collectFileStats(dir: string, baseDir: string): string[] {\n const entries: string[] = [];\n let files: string[];\n try {\n files = readdirSync(dir);\n } catch {\n return entries;\n }\n for (const file of files) {\n const fullPath = join(dir, file);\n let stat;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n if (SKIP_DIRS.has(file)) continue;\n entries.push(...collectFileStats(fullPath, baseDir));\n } else if (SOURCE_EXTENSIONS.test(file) && !TEST_FILE_PATTERN.test(file)) {\n const relativePath = relative(baseDir, fullPath);\n entries.push(`${relativePath}:${stat.mtimeMs}:${stat.size}`);\n }\n }\n return entries;\n}\n\n/** Compute a SHA-256 hash of the server source directory + options */\nexport function computeSourceHash(serverConfig: MionServerConfig, aotOptions?: AOTCacheOptions): string {\n const serverDir = dirname(resolve(serverConfig.startServerScript));\n const fileStats = collectFileStats(serverDir, serverDir);\n fileStats.sort();\n\n const hashInput = [\n ...fileStats,\n `cacheVersion:${AOT_DISK_CACHE_VERSION}`,\n `devtoolsVersion:${getDevtoolsVersion()}`,\n `excludedFns:${JSON.stringify((aotOptions?.excludedFns || []).slice().sort())}`,\n `excludedPureFns:${JSON.stringify((aotOptions?.excludedPureFns || []).slice().sort())}`,\n ].join('\\n');\n\n return createHash('sha256').update(hashInput).digest('hex');\n}\n\n/** Read and validate the disk cache file. Returns null on any error. */\nfunction readDiskCache(cacheDir: string): AOTDiskCacheFile | null {\n const cachePath = join(cacheDir, CACHE_FILENAME);\n try {\n const raw = readFileSync(cachePath, 'utf-8');\n const parsed = JSON.parse(raw);\n if (\n typeof parsed.cacheVersion === 'string' &&\n typeof parsed.hash === 'string' &&\n typeof parsed.jitFnsCode === 'string' &&\n typeof parsed.pureFnsCode === 'string' &&\n typeof parsed.routerCacheCode === 'string'\n ) {\n return parsed as AOTDiskCacheFile;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/** Write cache data to disk. Best-effort — logs warning on failure. */\nfunction writeDiskCache(cacheDir: string, hash: string, data: AOTCacheData): void {\n const cachePath = join(cacheDir, CACHE_FILENAME);\n try {\n mkdirSync(cacheDir, {recursive: true});\n const cacheFile: AOTDiskCacheFile = {\n cacheVersion: AOT_DISK_CACHE_VERSION,\n hash,\n jitFnsCode: data.jitFnsCode,\n pureFnsCode: data.pureFnsCode,\n routerCacheCode: data.routerCacheCode,\n };\n writeFileSync(cachePath, JSON.stringify(cacheFile), 'utf-8');\n } catch (err) {\n console.warn(`[mion] Warning: Could not write AOT disk cache: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\n/** Resolve the cache directory from options and Vite's cacheDir */\nexport function resolveCacheDir(options: AOTCacheOptions, viteCacheDir?: string): string {\n if (options.cache === false) return '';\n if (typeof options.cache === 'string') return resolve(options.cache);\n return viteCacheDir || resolve(process.cwd(), 'node_modules/.vite');\n}\n\n/** Load AOT caches from disk if valid, otherwise generate fresh and save to disk. */\nexport async function getOrGenerateAOTCaches(\n serverConfig: MionServerConfig,\n aotOptions: AOTCacheOptions | undefined,\n cacheDir: string\n): Promise<AOTCacheResult> {\n const forceRegenerate = process.env.MION_AOT_FORCE === 'true';\n // IPC mode always needs a live child process (the server), so skip disk caching entirely\n const isIPCMode = serverConfig.mode === 'IPC';\n const cachingEnabled = cacheDir !== '' && !forceRegenerate && !isIPCMode;\n\n let hash = '';\n if (cachingEnabled) {\n hash = computeSourceHash(serverConfig, aotOptions);\n const cached = readDiskCache(cacheDir);\n if (cached && cached.cacheVersion === AOT_DISK_CACHE_VERSION && cached.hash === hash) {\n console.log('[mion] AOT caches loaded from disk cache (source unchanged)');\n return {\n data: {\n jitFnsCode: cached.jitFnsCode,\n pureFnsCode: cached.pureFnsCode,\n routerCacheCode: cached.routerCacheCode,\n },\n };\n }\n }\n\n // Cache miss or caching disabled — generate fresh\n const result = await generateAOTCaches(serverConfig);\n\n if (cachingEnabled) {\n if (!hash) hash = computeSourceHash(serverConfig, aotOptions);\n writeDiskCache(cacheDir, hash, result.data);\n console.log('[mion] AOT caches saved to disk cache');\n }\n\n return result;\n}\n\n/** Update the disk cache after HMR regeneration */\nexport function updateDiskCache(\n serverConfig: MionServerConfig,\n aotOptions: AOTCacheOptions | undefined,\n data: AOTCacheData,\n cacheDir: string\n): void {\n if (!cacheDir || aotOptions?.cache === false) return;\n const hash = computeSourceHash(serverConfig, aotOptions);\n writeDiskCache(cacheDir, hash, data);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"aotDiskCache.js","sources":["../../../../../src/vite-plugin/aotDiskCache.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {createHash} from 'crypto';\nimport {readFileSync, writeFileSync, mkdirSync, readdirSync, statSync} from 'fs';\nimport {join, resolve, dirname, relative} from 'path';\nimport {AOTCacheOptions, MionServerConfig} from './types.ts';\nimport {generateAOTCaches, AOTCacheData, AOTCacheResult} from './aotCacheGenerator.ts';\n\n/** Schema for the on-disk cache file */\ninterface AOTDiskCacheFile {\n cacheVersion: string;\n hash: string;\n jitFnsCode: string;\n pureFnsCode: string;\n routerCacheCode: string;\n}\n\n/** Bump when mion internals change in a way that invalidates cached output */\nconst AOT_DISK_CACHE_VERSION = '2';\n\n/** Cache file name */\nconst CACHE_FILENAME = 'mion-aot-cache.json';\n\n/** Directories to skip when walking the server source tree */\nconst SKIP_DIRS = new Set(['node_modules', '.dist', 'dist', '.git', '.vite', 'build', 'coverage', '.coverage']);\n\n/** File extensions to include in hash computation */\nconst SOURCE_EXTENSIONS = /\\.(ts|tsx)$/;\n\n/** Test file patterns to exclude from hash computation */\nconst TEST_FILE_PATTERN = /\\.(spec|test)\\.(ts|tsx)$/;\n\n/** Read the devtools package version (cached after first call) */\nlet devtoolsVersion: string | null = null;\nfunction getDevtoolsVersion(): string {\n if (devtoolsVersion) return devtoolsVersion;\n try {\n const pkgPath = resolve(dirname(new URL(import.meta.url).pathname), '../../package.json');\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n devtoolsVersion = pkg.version || '0.0.0';\n } catch {\n devtoolsVersion = '0.0.0';\n }\n return devtoolsVersion as string;\n}\n\n/** Recursively collect file stats (relativePath:mtimeMs:size) for source files */\nfunction collectFileStats(dir: string, baseDir: string): string[] {\n const entries: string[] = [];\n let files: string[];\n try {\n files = readdirSync(dir);\n } catch {\n return entries;\n }\n for (const file of files) {\n const fullPath = join(dir, file);\n let stat;\n try {\n stat = statSync(fullPath);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n if (SKIP_DIRS.has(file)) continue;\n entries.push(...collectFileStats(fullPath, baseDir));\n } else if (SOURCE_EXTENSIONS.test(file) && !TEST_FILE_PATTERN.test(file)) {\n const relativePath = relative(baseDir, fullPath);\n entries.push(`${relativePath}:${stat.mtimeMs}:${stat.size}`);\n }\n }\n return entries;\n}\n\n/** Compute a SHA-256 hash of the server source directory + options */\nexport function computeSourceHash(serverConfig: MionServerConfig, aotOptions?: AOTCacheOptions): string {\n const serverDir = dirname(resolve(serverConfig.startScript));\n const fileStats = collectFileStats(serverDir, serverDir);\n fileStats.sort();\n\n const hashInput = [\n ...fileStats,\n `cacheVersion:${AOT_DISK_CACHE_VERSION}`,\n `devtoolsVersion:${getDevtoolsVersion()}`,\n `excludedFns:${JSON.stringify((aotOptions?.excludedFns || []).slice().sort())}`,\n `excludedPureFns:${JSON.stringify((aotOptions?.excludedPureFns || []).slice().sort())}`,\n `isClient:${!!aotOptions?.isClient}`,\n ].join('\\n');\n\n return createHash('sha256').update(hashInput).digest('hex');\n}\n\n/** Read and validate the disk cache file. Returns null on any error. */\nfunction readDiskCache(cacheDir: string): AOTDiskCacheFile | null {\n const cachePath = join(cacheDir, CACHE_FILENAME);\n try {\n const raw = readFileSync(cachePath, 'utf-8');\n const parsed = JSON.parse(raw);\n if (\n typeof parsed.cacheVersion === 'string' &&\n typeof parsed.hash === 'string' &&\n typeof parsed.jitFnsCode === 'string' &&\n typeof parsed.pureFnsCode === 'string' &&\n typeof parsed.routerCacheCode === 'string'\n ) {\n return parsed as AOTDiskCacheFile;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/** Write cache data to disk. Best-effort — logs warning on failure. */\nfunction writeDiskCache(cacheDir: string, hash: string, data: AOTCacheData): void {\n const cachePath = join(cacheDir, CACHE_FILENAME);\n try {\n mkdirSync(cacheDir, {recursive: true});\n const cacheFile: AOTDiskCacheFile = {\n cacheVersion: AOT_DISK_CACHE_VERSION,\n hash,\n jitFnsCode: data.jitFnsCode,\n pureFnsCode: data.pureFnsCode,\n routerCacheCode: data.routerCacheCode,\n };\n writeFileSync(cachePath, JSON.stringify(cacheFile), 'utf-8');\n } catch (err) {\n console.warn(`[mion] Warning: Could not write AOT disk cache: ${err instanceof Error ? err.message : String(err)}`);\n }\n}\n\n/** Resolve the cache directory from options and Vite's cacheDir */\nexport function resolveCacheDir(options: AOTCacheOptions, viteCacheDir?: string): string {\n if (options.cache === false) return '';\n if (typeof options.cache === 'string') return resolve(options.cache);\n return viteCacheDir || resolve(process.cwd(), 'node_modules/.vite');\n}\n\n/** Load AOT caches from disk if valid, otherwise generate fresh and save to disk. */\nexport async function getOrGenerateAOTCaches(\n serverConfig: MionServerConfig,\n aotOptions: AOTCacheOptions | undefined,\n cacheDir: string\n): Promise<AOTCacheResult> {\n const forceRegenerate = process.env.MION_AOT_FORCE === 'true';\n // IPC mode always needs a live child process (the server), so skip disk caching entirely\n const isIPCMode = serverConfig.runMode === 'childProcess';\n const cachingEnabled = cacheDir !== '' && !forceRegenerate && !isIPCMode;\n\n let hash = '';\n if (cachingEnabled) {\n hash = computeSourceHash(serverConfig, aotOptions);\n const cached = readDiskCache(cacheDir);\n if (cached && cached.cacheVersion === AOT_DISK_CACHE_VERSION && cached.hash === hash) {\n console.log('[mion] AOT caches loaded from disk cache (source unchanged)');\n return {\n data: {\n jitFnsCode: cached.jitFnsCode,\n pureFnsCode: cached.pureFnsCode,\n routerCacheCode: cached.routerCacheCode,\n },\n };\n }\n }\n\n // Cache miss or caching disabled — generate fresh\n const result = await generateAOTCaches(serverConfig, undefined, aotOptions?.isClient);\n\n if (cachingEnabled) {\n if (!hash) hash = computeSourceHash(serverConfig, aotOptions);\n writeDiskCache(cacheDir, hash, result.data);\n console.log('[mion] AOT caches saved to disk cache');\n }\n\n return result;\n}\n\n/** Update the disk cache after HMR regeneration */\nexport function updateDiskCache(\n serverConfig: MionServerConfig,\n aotOptions: AOTCacheOptions | undefined,\n data: AOTCacheData,\n cacheDir: string\n): void {\n if (!cacheDir || aotOptions?.cache === false) return;\n const hash = computeSourceHash(serverConfig, aotOptions);\n writeDiskCache(cacheDir, hash, data);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,8 @@ export declare const VIRTUAL_AOT_JIT_FNS = "virtual:mion-aot/jit-fns";
|
|
|
4
4
|
export declare const VIRTUAL_AOT_PURE_FNS = "virtual:mion-aot/pure-fns";
|
|
5
5
|
export declare const VIRTUAL_AOT_ROUTER_CACHE = "virtual:mion-aot/router-cache";
|
|
6
6
|
export declare const VIRTUAL_AOT_CACHES = "virtual:mion-aot/caches";
|
|
7
|
+
export declare const AOT_CACHES_SHIM = "@mionjs/core/aot-caches";
|
|
8
|
+
export declare const SERVER_PURE_FNS_SHIM = "@mionjs/core/server-pure-fns";
|
|
7
9
|
export declare const PURE_SERVER_FN_NAMESPACE = "pureServerFn";
|
|
8
10
|
export declare function resolveVirtualId(id: string): string;
|
|
9
11
|
export declare const REFLECTION_MODULES: string[];
|
|
@@ -4,6 +4,8 @@ const VIRTUAL_AOT_JIT_FNS = "virtual:mion-aot/jit-fns";
|
|
|
4
4
|
const VIRTUAL_AOT_PURE_FNS = "virtual:mion-aot/pure-fns";
|
|
5
5
|
const VIRTUAL_AOT_ROUTER_CACHE = "virtual:mion-aot/router-cache";
|
|
6
6
|
const VIRTUAL_AOT_CACHES = "virtual:mion-aot/caches";
|
|
7
|
+
const AOT_CACHES_SHIM = "@mionjs/core/aot-caches";
|
|
8
|
+
const SERVER_PURE_FNS_SHIM = "@mionjs/core/server-pure-fns";
|
|
7
9
|
const PURE_SERVER_FN_NAMESPACE = "pureServerFn";
|
|
8
10
|
function resolveVirtualId(id) {
|
|
9
11
|
return "\0" + id + ".ts";
|
|
@@ -11,9 +13,11 @@ function resolveVirtualId(id) {
|
|
|
11
13
|
const REFLECTION_MODULES = ["@mionjs/run-types", "@deepkit/type", "@deepkit/core"];
|
|
12
14
|
const VIRTUAL_STUB_PREFIX = "virtual:mion-stub/";
|
|
13
15
|
export {
|
|
16
|
+
AOT_CACHES_SHIM,
|
|
14
17
|
BODY_HASH_LENGTH,
|
|
15
18
|
PURE_SERVER_FN_NAMESPACE,
|
|
16
19
|
REFLECTION_MODULES,
|
|
20
|
+
SERVER_PURE_FNS_SHIM,
|
|
17
21
|
VIRTUAL_AOT_CACHES,
|
|
18
22
|
VIRTUAL_AOT_JIT_FNS,
|
|
19
23
|
VIRTUAL_AOT_PURE_FNS,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sources":["../../../../../src/vite-plugin/constants.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nexport const BODY_HASH_LENGTH = 14;\n\n/** Virtual module ID for the pure functions registry (client-extracted pure functions) */\nexport const VIRTUAL_SERVER_PURE_FNS = 'virtual:mion-server-pure-fns';\n\n// ============ AOT Virtual Modules ============\n\n/** Virtual module ID for JIT functions + pure functions cache (from running the router) */\nexport const VIRTUAL_AOT_JIT_FNS = 'virtual:mion-aot/jit-fns';\n\n/** Virtual module ID for pure functions cache (standalone, from client AST extraction) */\nexport const VIRTUAL_AOT_PURE_FNS = 'virtual:mion-aot/pure-fns';\n\n/** Virtual module ID for router methods cache */\nexport const VIRTUAL_AOT_ROUTER_CACHE = 'virtual:mion-aot/router-cache';\n\n/** Virtual module ID for combined AOT caches (re-exports all 3 cache modules) */\nexport const VIRTUAL_AOT_CACHES = 'virtual:mion-aot/caches';\n\n/** The namespace used for all pureServerFn functions */\nexport const PURE_SERVER_FN_NAMESPACE = 'pureServerFn';\n\n/** Resolves a virtual module ID to its internal Vite ID (\\0 prefix + .ts extension) */\nexport function resolveVirtualId(id: string): string {\n return '\\0' + id + '.ts';\n}\n\n// ============ Reflection Stubs (for excludeReflection) ============\n\n/** Modules stubbed out when excludeReflection is enabled (not needed at runtime in AOT mode) */\nexport const REFLECTION_MODULES = ['@mionjs/run-types', '@deepkit/type', '@deepkit/core'];\n\n/** Prefix for virtual stub module IDs */\nexport const VIRTUAL_STUB_PREFIX = 'virtual:mion-stub/';\n\n// Purity validation constants (ALLOWED_GLOBALS, FORBIDDEN_IDENTIFIERS, FACTORY_FORBIDDEN_IDENTIFIERS)\n// are in ../pureFns/purityRules.ts, shared with the eslint plugin.\n"],"names":[],"mappings":"AAOO,MAAM,mBAAmB;AAGzB,MAAM,0BAA0B;AAKhC,MAAM,sBAAsB;AAG5B,MAAM,uBAAuB;AAG7B,MAAM,2BAA2B;AAGjC,MAAM,qBAAqB;AAG3B,MAAM,2BAA2B;AAGjC,SAAS,iBAAiB,IAAoB;AACjD,SAAO,OAAO,KAAK;AACvB;AAKO,MAAM,qBAAqB,CAAC,qBAAqB,iBAAiB,eAAe;AAGjF,MAAM,sBAAsB;"}
|
|
1
|
+
{"version":3,"file":"constants.js","sources":["../../../../../src/vite-plugin/constants.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nexport const BODY_HASH_LENGTH = 14;\n\n/** Virtual module ID for the pure functions registry (client-extracted pure functions) */\nexport const VIRTUAL_SERVER_PURE_FNS = 'virtual:mion-server-pure-fns';\n\n// ============ AOT Virtual Modules ============\n\n/** Virtual module ID for JIT functions + pure functions cache (from running the router) */\nexport const VIRTUAL_AOT_JIT_FNS = 'virtual:mion-aot/jit-fns';\n\n/** Virtual module ID for pure functions cache (standalone, from client AST extraction) */\nexport const VIRTUAL_AOT_PURE_FNS = 'virtual:mion-aot/pure-fns';\n\n/** Virtual module ID for router methods cache */\nexport const VIRTUAL_AOT_ROUTER_CACHE = 'virtual:mion-aot/router-cache';\n\n/** Virtual module ID for combined AOT caches (re-exports all 3 cache modules) */\nexport const VIRTUAL_AOT_CACHES = 'virtual:mion-aot/caches';\n\n/** The real module that acts as a shim for AOT caches (empty caches). Swapped by the plugin when AOT is enabled. */\nexport const AOT_CACHES_SHIM = '@mionjs/core/aot-caches';\n\n/** The real module that acts as a shim for server pure functions (empty cache). Swapped by the plugin when serverPureFunctions is enabled. */\nexport const SERVER_PURE_FNS_SHIM = '@mionjs/core/server-pure-fns';\n\n/** The namespace used for all pureServerFn functions */\nexport const PURE_SERVER_FN_NAMESPACE = 'pureServerFn';\n\n/** Resolves a virtual module ID to its internal Vite ID (\\0 prefix + .ts extension) */\nexport function resolveVirtualId(id: string): string {\n return '\\0' + id + '.ts';\n}\n\n// ============ Reflection Stubs (for excludeReflection) ============\n\n/** Modules stubbed out when excludeReflection is enabled (not needed at runtime in AOT mode) */\nexport const REFLECTION_MODULES = ['@mionjs/run-types', '@deepkit/type', '@deepkit/core'];\n\n/** Prefix for virtual stub module IDs */\nexport const VIRTUAL_STUB_PREFIX = 'virtual:mion-stub/';\n\n// Purity validation constants (ALLOWED_GLOBALS, FORBIDDEN_IDENTIFIERS, FACTORY_FORBIDDEN_IDENTIFIERS)\n// are in ../pureFns/purityRules.ts, shared with the eslint plugin.\n"],"names":[],"mappings":"AAOO,MAAM,mBAAmB;AAGzB,MAAM,0BAA0B;AAKhC,MAAM,sBAAsB;AAG5B,MAAM,uBAAuB;AAG7B,MAAM,2BAA2B;AAGjC,MAAM,qBAAqB;AAG3B,MAAM,kBAAkB;AAGxB,MAAM,uBAAuB;AAG7B,MAAM,2BAA2B;AAGjC,SAAS,iBAAiB,IAAoB;AACjD,SAAO,OAAO,KAAK;AACvB;AAKO,MAAM,qBAAqB,CAAC,qBAAqB,iBAAiB,eAAe;AAGjF,MAAM,sBAAsB;"}
|
|
@@ -8,11 +8,11 @@ export interface MionPluginOptions {
|
|
|
8
8
|
export declare function mionVitePlugin(options: MionPluginOptions): {
|
|
9
9
|
name: string;
|
|
10
10
|
enforce: "pre";
|
|
11
|
-
config(config: any): void;
|
|
11
|
+
config(config: any, env: any): void;
|
|
12
12
|
configResolved(config: any): void;
|
|
13
13
|
buildStart(): Promise<void>;
|
|
14
14
|
configureServer(server: any): void;
|
|
15
|
-
resolveId(id: any): string | null;
|
|
15
|
+
resolveId(id: any, importer: any): string | null;
|
|
16
16
|
load(id: any): Promise<string | {
|
|
17
17
|
code: string;
|
|
18
18
|
syntheticNamedExports: boolean;
|