@moltn/vite 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -2
- package/dist/index.d.ts +17 -0
- package/dist/index.js +113 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.js +10 -0
- package/package.json +49 -4
package/README.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @moltn/vite
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Vite plugin for [moltn](https://crates.io/crates/moltn-cli): builds an Inertia v3 server-side rendering bundle that runs **inside a Rust binary** (embedded JavaScript engine, no Node server).
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { defineConfig } from 'vite'
|
|
7
|
+
import vue from '@vitejs/plugin-vue'
|
|
8
|
+
import inertia from '@inertiajs/vite'
|
|
9
|
+
import moltn from '@moltn/vite'
|
|
10
|
+
|
|
11
|
+
export default defineConfig({
|
|
12
|
+
plugins: [vue(), inertia(), moltn()],
|
|
13
|
+
})
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- One self-contained `dist/ssr/ssr.js`, no imports; Inertia's `createServer` is swapped automatically, no `ssr.ts` to write.
|
|
17
|
+
- A static import of a Node module in a page fails the build with the file or package to blame.
|
|
18
|
+
- Works the same with React and Svelte.
|
|
19
|
+
|
|
20
|
+
## Part of moltn
|
|
21
|
+
|
|
22
|
+
Rust + Inertia v3 (Vue, React, Svelte) with server-side rendering inside the binary.
|
|
23
|
+
Guide (English and French): <https://docs.moltn.eu>. Source: <https://github.com/moltn-dev/moltn-libs>.
|
|
24
|
+
|
|
25
|
+
## License
|
|
26
|
+
|
|
27
|
+
MIT OR Apache-2.0.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
/** Options of `moltn()`. */
|
|
3
|
+
export interface MoltnOptions {
|
|
4
|
+
/** Application entry, shared by the client and SSR builds. Default: `src/app.ts`. */
|
|
5
|
+
entry?: string;
|
|
6
|
+
/** Prefix under which the Rust application serves the assets (`asset_base` of `ViteRootView`). Default: `/build/`. */
|
|
7
|
+
assetBase?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Complements `@inertiajs/vite` so that the SSR bundle runs in moltn's embedded
|
|
11
|
+
* JavaScript engine: a single `dist/ssr/ssr.js` file, with no imports and no Node modules.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* plugins: [vue(), inertia(), moltn()]
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export default function moltn(options?: MoltnOptions): Plugin;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { builtinModules } from 'node:module';
|
|
2
|
+
import { posix } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
const INERTIA_SERVER_MODULES = ['@inertiajs/vue3/server', '@inertiajs/react/server', '@inertiajs/svelte/server'];
|
|
5
|
+
const SERVER_SHIM = fileURLToPath(new URL('./server.js', import.meta.url));
|
|
6
|
+
const SSR_CONDITIONS = ['worker', 'production'];
|
|
7
|
+
const NODE_BUILTINS = new Set(builtinModules.flatMap((m) => [m, `node:${m}`]));
|
|
8
|
+
const UNAVAILABLE_NODE_MODULE = '\0moltn-node-unavailable:';
|
|
9
|
+
/**
|
|
10
|
+
* Complements `@inertiajs/vite` so that the SSR bundle runs in moltn's embedded
|
|
11
|
+
* JavaScript engine: a single `dist/ssr/ssr.js` file, with no imports and no Node modules.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* plugins: [vue(), inertia(), moltn()]
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export default function moltn(options = {}) {
|
|
18
|
+
const entry = options.entry ?? 'src/app.ts';
|
|
19
|
+
const assetBase = options.assetBase ?? '/build/';
|
|
20
|
+
let ssrBuild = false;
|
|
21
|
+
let root = process.cwd();
|
|
22
|
+
return {
|
|
23
|
+
name: '@moltn/vite',
|
|
24
|
+
config(config, env) {
|
|
25
|
+
ssrBuild = Boolean(env.isSsrBuild);
|
|
26
|
+
const input = config.build?.rolldownOptions?.input ?? config.build?.rollupOptions?.input;
|
|
27
|
+
if (!ssrBuild) {
|
|
28
|
+
return {
|
|
29
|
+
// Seulement au build : le serveur de dev sert à la racine.
|
|
30
|
+
base: env.command === 'build' ? (config.base ?? assetBase) : undefined,
|
|
31
|
+
// En dev, la page vient de l'application Rust : les URL d'assets (CSS, images)
|
|
32
|
+
// doivent viser le serveur Vite, pas l'origine de la page.
|
|
33
|
+
server: env.command === 'serve'
|
|
34
|
+
? { origin: config.server?.origin ?? `http://localhost:${config.server?.port ?? 5173}` }
|
|
35
|
+
: undefined,
|
|
36
|
+
build: {
|
|
37
|
+
outDir: config.build?.outDir ?? 'dist/client',
|
|
38
|
+
manifest: config.build?.manifest ?? true,
|
|
39
|
+
rolldownOptions: input ? undefined : { input: entry },
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
resolve: { alias: INERTIA_SERVER_MODULES.map((find) => ({ find, replacement: SERVER_SHIM })) },
|
|
45
|
+
ssr: {
|
|
46
|
+
noExternal: true,
|
|
47
|
+
target: 'webworker',
|
|
48
|
+
// « worker » et non « browser » : Svelte y sert son runtime serveur.
|
|
49
|
+
resolve: { conditions: SSR_CONDITIONS, externalConditions: SSR_CONDITIONS },
|
|
50
|
+
},
|
|
51
|
+
define: { 'process.env.NODE_ENV': '"production"' },
|
|
52
|
+
build: {
|
|
53
|
+
outDir: config.build?.outDir ?? 'dist/ssr',
|
|
54
|
+
sourcemap: false,
|
|
55
|
+
rolldownOptions: { output: { entryFileNames: 'ssr.js', codeSplitting: false } },
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
configResolved(config) {
|
|
60
|
+
root = config.root;
|
|
61
|
+
},
|
|
62
|
+
// Avant tout autre résolveur : un module Node ne doit jamais être remplacé en silence.
|
|
63
|
+
resolveId: {
|
|
64
|
+
order: 'pre',
|
|
65
|
+
handler(source, importer, options) {
|
|
66
|
+
if (!ssrBuild || !NODE_BUILTINS.has(source))
|
|
67
|
+
return null;
|
|
68
|
+
// `import()` dynamique : dépendance optionnelle (ex. Svelte et node:async_hooks).
|
|
69
|
+
// Le module de remplacement fait échouer l'import, comme dans un navigateur.
|
|
70
|
+
if (options.kind === 'dynamic-import') {
|
|
71
|
+
this.info(`optional import of Node module "${source}" by ${importer ? describe(importer, root) : 'a module'}: it will fail in the embedded SSR engine.`);
|
|
72
|
+
return UNAVAILABLE_NODE_MODULE + source;
|
|
73
|
+
}
|
|
74
|
+
this.error(nodeModuleMessage(source, importer, root));
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
load(id) {
|
|
78
|
+
if (!id.startsWith(UNAVAILABLE_NODE_MODULE))
|
|
79
|
+
return null;
|
|
80
|
+
const source = id.slice(UNAVAILABLE_NODE_MODULE.length);
|
|
81
|
+
return `throw new Error(${JSON.stringify(`Node module "${source}" is not available in the embedded SSR engine`)})`;
|
|
82
|
+
},
|
|
83
|
+
generateBundle(_, bundle) {
|
|
84
|
+
if (!ssrBuild)
|
|
85
|
+
return;
|
|
86
|
+
const chunks = Object.values(bundle).filter((file) => file.type === 'chunk');
|
|
87
|
+
if (chunks.length !== 1) {
|
|
88
|
+
this.error(`SSR bundle split into ${chunks.length} files; moltn expects a single file (${chunks.map((c) => c.fileName).join(', ')}).`);
|
|
89
|
+
}
|
|
90
|
+
// Les imports dynamiques inlinés (pages chargées à la demande) pointent vers le fichier lui-même.
|
|
91
|
+
const imports = chunks.flatMap((c) => [...c.imports, ...c.dynamicImports].filter((i) => i !== c.fileName));
|
|
92
|
+
if (imports.length > 0) {
|
|
93
|
+
this.error(`the SSR bundle still imports modules (${imports.join(', ')}); the embedded engine loads none.`);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function nodeModuleMessage(source, importer, root) {
|
|
99
|
+
return [
|
|
100
|
+
`Node module "${source}" is imported by ${importer ? describe(importer, root) : 'an unknown module'}.`,
|
|
101
|
+
'moltn renders on the server in an embedded JavaScript engine, without Node: pages and their dependencies',
|
|
102
|
+
'must not use any Node module (fs, path, stream, crypto…). Replace this package, or import it',
|
|
103
|
+
'in the browser only (onMounted / useEffect / onMount).',
|
|
104
|
+
].join('\n');
|
|
105
|
+
}
|
|
106
|
+
/** "package x (path)" for a dependency, "file src/…" for the project. */
|
|
107
|
+
function describe(id, root) {
|
|
108
|
+
const path = id.split('?')[0].replace(/\\/g, '/');
|
|
109
|
+
const pkg = path.match(/.*node_modules\/((?:@[^/]+\/)?[^/]+)/)?.[1];
|
|
110
|
+
if (pkg)
|
|
111
|
+
return `package ${pkg}\n (${path})`;
|
|
112
|
+
return `file ${posix.relative(root.replace(/\\/g, '/'), path)}`;
|
|
113
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import 'core-js/actual/url';
|
|
2
|
+
import 'core-js/actual/url-search-params';
|
|
3
|
+
/** Render function passed by Inertia: page object → `{ head, body }`. */
|
|
4
|
+
export type Render = (page: unknown) => Promise<{
|
|
5
|
+
head: string[];
|
|
6
|
+
body: string;
|
|
7
|
+
}>;
|
|
8
|
+
declare global {
|
|
9
|
+
var __moltnRender: Render | undefined;
|
|
10
|
+
}
|
|
11
|
+
/** Same signature as Inertia's `createServer`; options (port, cluster…) are ignored. */
|
|
12
|
+
export default function createServer(render: Render, _options?: unknown): Render;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Remplace `@inertiajs/<framework>/server` dans le build SSR : au lieu d'ouvrir un serveur
|
|
2
|
+
// HTTP Node, la fonction de rendu est exposée au moteur JS embarqué côté Rust (moltn-ssr).
|
|
3
|
+
// URL / URLSearchParams : absents du moteur, utilisés par <Link> d'Inertia pendant le rendu.
|
|
4
|
+
import 'core-js/actual/url';
|
|
5
|
+
import 'core-js/actual/url-search-params';
|
|
6
|
+
/** Same signature as Inertia's `createServer`; options (port, cluster…) are ignored. */
|
|
7
|
+
export default function createServer(render, _options) {
|
|
8
|
+
globalThis.__moltnRender = render;
|
|
9
|
+
return render;
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moltn/vite",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vite plugin for moltn: an SSR bundle that runs in the JavaScript engine embedded in a Rust binary.",
|
|
5
|
+
"license": "MIT OR Apache-2.0",
|
|
6
|
+
"homepage": "https://docs.moltn.eu",
|
|
7
|
+
"repository": { "type": "git", "url": "git+https://github.com/moltn-dev/moltn-libs.git", "directory": "packages/vite" },
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./server": {
|
|
15
|
+
"types": "./dist/server.d.ts",
|
|
16
|
+
"import": "./dist/server.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p .",
|
|
25
|
+
"typecheck": "tsc -p . --noEmit",
|
|
26
|
+
"test": "node --test test/*.test.mjs",
|
|
27
|
+
"prepack": "tsc -p ."
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"core-js": "^3.51.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@inertiajs/vite": "^3.11.0",
|
|
34
|
+
"vite": "^8.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^24.0.0",
|
|
38
|
+
"typescript": "^7.0.2",
|
|
39
|
+
"vite": "^8.3.8"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"vite",
|
|
43
|
+
"inertia",
|
|
44
|
+
"ssr",
|
|
45
|
+
"rust",
|
|
46
|
+
"quickjs"
|
|
47
|
+
],
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
}
|
|
51
|
+
}
|