@octanejs/vite-plugin 0.1.5 → 0.1.6
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/package.json +16 -4
- package/src/config-entry.js +15 -0
- package/src/constants.js +1 -3
- package/src/index.js +74 -24
- package/src/load-config.js +61 -81
- package/src/project-codegen.js +10 -218
- package/src/resolve-config.js +1 -171
- package/src/routes.js +1 -133
- package/src/server/component-wrappers.js +5 -56
- package/src/server/html-stream.js +1 -0
- package/src/server/html-template.js +9 -0
- package/src/server/middleware.js +1 -127
- package/src/server/node-http.js +1 -188
- package/src/server/production.js +1 -286
- package/src/server/render-route.js +66 -41
- package/src/server/router.js +1 -123
- package/src/server/server-route.js +1 -47
- package/src/server/virtual-entry.js +1 -184
- package/types/index.d.ts +30 -260
- package/types/node.d.ts +1 -31
- package/types/production.d.ts +1 -71
- package/CHANGELOG.md +0 -254
- package/tests/_fixtures/app/index.html +0 -11
- package/tests/_fixtures/app/octane.config.ts +0 -14
- package/tests/_fixtures/app/package.json +0 -7
- package/tests/_fixtures/app/src/Layout.tsrx +0 -6
- package/tests/_fixtures/app/src/Page.tsrx +0 -17
- package/tests/_fixtures/app/vite.config.ts +0 -12
- package/tests/handler.test.ts +0 -134
- package/tests/plugin.test.ts +0 -155
- package/tests/production.test.ts +0 -157
- package/tsconfig.typecheck.json +0 -18
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/vite-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22"
|
|
8
|
+
},
|
|
9
|
+
"description": "Vite integration for Octane (SPA compilation plus optional routing, SSR, and hydration)",
|
|
7
10
|
"author": {
|
|
8
11
|
"name": "Dominic Gannaway",
|
|
9
12
|
"email": "dg@domgan.com"
|
|
@@ -16,6 +19,10 @@
|
|
|
16
19
|
"url": "git+https://github.com/octanejs/octane.git",
|
|
17
20
|
"directory": "packages/vite-plugin-octane"
|
|
18
21
|
},
|
|
22
|
+
"files": [
|
|
23
|
+
"src",
|
|
24
|
+
"types"
|
|
25
|
+
],
|
|
19
26
|
"module": "src/index.js",
|
|
20
27
|
"main": "src/index.js",
|
|
21
28
|
"bin": {
|
|
@@ -40,11 +47,16 @@
|
|
|
40
47
|
},
|
|
41
48
|
"dependencies": {
|
|
42
49
|
"@ripple-ts/adapter": "^0.3.86",
|
|
43
|
-
"
|
|
50
|
+
"@octanejs/app-core": "0.0.2"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"vite": "^8.0.16",
|
|
54
|
+
"octane": "0.1.6"
|
|
44
55
|
},
|
|
45
56
|
"devDependencies": {
|
|
46
57
|
"@types/node": "^24.3.0",
|
|
47
58
|
"type-fest": "^5.6.0",
|
|
48
|
-
"vite": "^8.0.16"
|
|
59
|
+
"vite": "^8.0.16",
|
|
60
|
+
"octane": "0.1.6"
|
|
49
61
|
}
|
|
50
62
|
}
|
package/src/config-entry.js
CHANGED
|
@@ -13,6 +13,21 @@
|
|
|
13
13
|
|
|
14
14
|
export { RenderRoute, ServerRoute } from './routes.js';
|
|
15
15
|
export { resolveOctaneConfig } from './resolve-config.js';
|
|
16
|
+
export { OCTANE_NONCE_STATE_KEY } from './constants.js';
|
|
17
|
+
export {
|
|
18
|
+
DEFAULT_OUTDIR,
|
|
19
|
+
ENTRY_FILENAME,
|
|
20
|
+
compose,
|
|
21
|
+
createContext,
|
|
22
|
+
createRouter,
|
|
23
|
+
get_component_export,
|
|
24
|
+
get_route_entry_export_name,
|
|
25
|
+
get_route_entry_id,
|
|
26
|
+
get_route_entry_path,
|
|
27
|
+
handleServerRoute,
|
|
28
|
+
is_rpc_request,
|
|
29
|
+
runMiddlewareChain,
|
|
30
|
+
} from '@octanejs/app-core';
|
|
16
31
|
|
|
17
32
|
// Mirrors src/index.js — enforce types / DX only.
|
|
18
33
|
export function defineConfig(/** @type {any} */ options) {
|
package/src/constants.js
CHANGED
package/src/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import fs from 'node:fs';
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
9
10
|
|
|
10
11
|
import { octane as octaneCompiler } from 'octane/compiler/vite';
|
|
11
12
|
|
|
@@ -13,12 +14,14 @@ import { createRouter } from './server/router.js';
|
|
|
13
14
|
import { createContext, runMiddlewareChain } from './server/middleware.js';
|
|
14
15
|
import { handleRenderRoute } from './server/render-route.js';
|
|
15
16
|
import { handleServerRoute } from './server/server-route.js';
|
|
17
|
+
import { HYDRATION_NONCE_PLACEHOLDER, injectHydrationEntry } from './server/html-template.js';
|
|
16
18
|
import { generateServerEntry } from './server/virtual-entry.js';
|
|
17
19
|
import { nodeRequestToWebRequest, sendWebResponse } from './server/node-http.js';
|
|
18
20
|
import { ENTRY_FILENAME } from './constants.js';
|
|
19
21
|
import {
|
|
20
22
|
getOctaneConfigPath,
|
|
21
23
|
loadOctaneConfig,
|
|
24
|
+
loadOctaneConfigWithMetadata,
|
|
22
25
|
resolveOctaneConfig,
|
|
23
26
|
octaneConfigExists,
|
|
24
27
|
} from './load-config.js';
|
|
@@ -37,16 +40,35 @@ import { get_route_entry_path } from './routes.js';
|
|
|
37
40
|
|
|
38
41
|
// Re-export route classes + config helpers (public API surface).
|
|
39
42
|
export { RenderRoute, ServerRoute } from './routes.js';
|
|
43
|
+
export { OCTANE_NONCE_STATE_KEY } from './constants.js';
|
|
44
|
+
export {
|
|
45
|
+
DEFAULT_OUTDIR,
|
|
46
|
+
ENTRY_FILENAME,
|
|
47
|
+
compose,
|
|
48
|
+
createContext,
|
|
49
|
+
createRouter,
|
|
50
|
+
get_component_export,
|
|
51
|
+
get_route_entry_export_name,
|
|
52
|
+
get_route_entry_id,
|
|
53
|
+
get_route_entry_path,
|
|
54
|
+
handleServerRoute,
|
|
55
|
+
is_rpc_request,
|
|
56
|
+
runMiddlewareChain,
|
|
57
|
+
} from '@octanejs/app-core';
|
|
40
58
|
export {
|
|
41
59
|
getOctaneConfigPath,
|
|
42
60
|
loadOctaneConfig,
|
|
61
|
+
loadOctaneConfigWithMetadata,
|
|
43
62
|
resolveOctaneConfig,
|
|
44
63
|
octaneConfigExists,
|
|
45
64
|
} from './load-config.js';
|
|
46
65
|
|
|
47
66
|
const VIRTUAL_HYDRATE_ID = 'virtual:octane-hydrate';
|
|
48
67
|
const RESOLVED_VIRTUAL_HYDRATE_ID = '\0virtual:octane-hydrate';
|
|
49
|
-
const
|
|
68
|
+
const requireFromPlugin = createRequire(import.meta.url);
|
|
69
|
+
// Mirrors octane/compiler/vite's full-compiler surface. Keeping this list in
|
|
70
|
+
// sync is especially important for production `module server` discovery.
|
|
71
|
+
const OCTANE_EXTENSIONS = ['.tsrx', '.tsx'];
|
|
50
72
|
|
|
51
73
|
/**
|
|
52
74
|
* @param {string} file_name
|
|
@@ -147,7 +169,9 @@ function has_route_config(config) {
|
|
|
147
169
|
}
|
|
148
170
|
|
|
149
171
|
/**
|
|
150
|
-
* The
|
|
172
|
+
* The recommended Octane Vite integration. With no octane.config.ts it behaves
|
|
173
|
+
* as a compiler plugin inside a normal Vite SPA; configured routes activate the
|
|
174
|
+
* metaframework layer.
|
|
151
175
|
*
|
|
152
176
|
* Returns an ARRAY: `[octaneCompiler({ hmr }), metaPlugin]`. The first element is
|
|
153
177
|
* octane/compiler's transform plugin — it owns ALL `.tsrx` compilation, picking
|
|
@@ -184,6 +208,8 @@ export function octane(inlineOptions = {}) {
|
|
|
184
208
|
let buildOctaneConfig = null;
|
|
185
209
|
/** @type {string[]} Module paths the generated client entry maps statically (build only) */
|
|
186
210
|
let staticEntries = [];
|
|
211
|
+
/** @type {Set<string>} Vite-root paths of modules containing `module server` */
|
|
212
|
+
const serverModuleModules = new Set();
|
|
187
213
|
|
|
188
214
|
/** @type {Plugin} */
|
|
189
215
|
const metaPlugin = {
|
|
@@ -196,33 +222,31 @@ export function octane(inlineOptions = {}) {
|
|
|
196
222
|
async config(userConfig, env) {
|
|
197
223
|
isBuild = env?.command === 'build';
|
|
198
224
|
isSSRBuild = !!userConfig.build?.ssr;
|
|
225
|
+
const projectRoot = userConfig.root ? path.resolve(userConfig.root) : process.cwd();
|
|
226
|
+
const hasOctaneConfig = octaneConfigExists(projectRoot);
|
|
199
227
|
|
|
200
228
|
const exclude = userConfig.optimizeDeps?.exclude || [];
|
|
201
229
|
const base = {
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
230
|
+
// A zero-config project is a normal Vite SPA: leave appType unset so
|
|
231
|
+
// Vite retains its HTML transform and history fallback. An Octane app
|
|
232
|
+
// config opts into framework routing, where SSR owns navigation and the
|
|
233
|
+
// SPA fallback must not mask unmatched routes. Respect an explicit user
|
|
234
|
+
// appType, and leave `vite preview` alone — production SSR is previewed
|
|
205
235
|
// with `octane-preview` (it serves dist/server), not `vite preview`.
|
|
206
|
-
...(userConfig.appType === undefined && !env?.isPreview
|
|
236
|
+
...(hasOctaneConfig && userConfig.appType === undefined && !env?.isPreview
|
|
207
237
|
? { appType: /** @type {const} */ ('custom') }
|
|
208
238
|
: {}),
|
|
209
239
|
optimizeDeps: {
|
|
210
240
|
exclude: [
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
...new Set([
|
|
214
|
-
...exclude,
|
|
215
|
-
'octane',
|
|
216
|
-
'octane/compiler',
|
|
217
|
-
'@octanejs/tanstack-query',
|
|
218
|
-
...SERVER_ONLY_ADAPTER_IDS,
|
|
219
|
-
]),
|
|
241
|
+
// The compiler plugin has already added every manifest-discovered
|
|
242
|
+
// raw Octane dependency to `exclude`; preserve that list here.
|
|
243
|
+
...new Set([...exclude, 'octane', 'octane/compiler', ...SERVER_ONLY_ADAPTER_IDS]),
|
|
220
244
|
],
|
|
221
245
|
},
|
|
222
|
-
//
|
|
223
|
-
//
|
|
246
|
+
// Raw binding packages are supplied recursively by the compiler plugin;
|
|
247
|
+
// these core entrypoints remain explicit metaframework dependencies.
|
|
224
248
|
ssr: {
|
|
225
|
-
noExternal: ['octane', 'octane/compiler'
|
|
249
|
+
noExternal: ['octane', 'octane/compiler'],
|
|
226
250
|
},
|
|
227
251
|
};
|
|
228
252
|
|
|
@@ -230,8 +254,7 @@ export function octane(inlineOptions = {}) {
|
|
|
230
254
|
// build.ssr, so it skips this): route the client bundle to
|
|
231
255
|
// `{outDir}/client` and emit a manifest for the server's asset map.
|
|
232
256
|
if (isBuild && !isSSRBuild) {
|
|
233
|
-
|
|
234
|
-
if (octaneConfigExists(projectRoot)) {
|
|
257
|
+
if (hasOctaneConfig) {
|
|
235
258
|
buildOctaneConfig = await loadOctaneConfig(projectRoot);
|
|
236
259
|
if (has_route_config(buildOctaneConfig)) {
|
|
237
260
|
if (!fs.existsSync(path.join(projectRoot, 'index.html'))) {
|
|
@@ -267,11 +290,16 @@ export function octane(inlineOptions = {}) {
|
|
|
267
290
|
*/
|
|
268
291
|
buildStart() {
|
|
269
292
|
if (!isBuild || isSSRBuild || !has_route_config(buildOctaneConfig)) return;
|
|
293
|
+
serverModuleModules.clear();
|
|
270
294
|
const cfg = /** @type {ResolvedOctaneConfig} */ (buildOctaneConfig);
|
|
271
295
|
const entries = cfg.router.routes
|
|
272
296
|
.filter((r) => r.type === 'render')
|
|
273
297
|
.flatMap((r) => [get_route_entry_path(/** @type {RenderRoute} */ (r).entry), r.layout]);
|
|
274
298
|
if (cfg.router.preHydrate) entries.push(cfg.router.preHydrate);
|
|
299
|
+
entries.push(
|
|
300
|
+
get_route_entry_path(cfg.rootBoundary.pending),
|
|
301
|
+
get_route_entry_path(cfg.rootBoundary.catch),
|
|
302
|
+
);
|
|
275
303
|
staticEntries = [...new Set(entries.filter((e) => typeof e === 'string'))];
|
|
276
304
|
},
|
|
277
305
|
|
|
@@ -313,6 +341,22 @@ export function octane(inlineOptions = {}) {
|
|
|
313
341
|
return null;
|
|
314
342
|
},
|
|
315
343
|
|
|
344
|
+
/**
|
|
345
|
+
* Observe the compiler's client output after the pre-transform. A generated
|
|
346
|
+
* __serverRpc call is an exact, syntax-level signal that this source owns a
|
|
347
|
+
* `module server`; production uses the collected paths as static SSR imports.
|
|
348
|
+
*/
|
|
349
|
+
transform(code, id, options) {
|
|
350
|
+
if (!isBuild || options?.ssr || !is_octane_module_path(id.split('?')[0])) return null;
|
|
351
|
+
if (!code.includes('_$__serverRpc(')) return null;
|
|
352
|
+
const file = id.split('?')[0];
|
|
353
|
+
const relative = path.relative(root, file);
|
|
354
|
+
const isWithinRoot =
|
|
355
|
+
relative !== '..' && !relative.startsWith('..' + path.sep) && !path.isAbsolute(relative);
|
|
356
|
+
serverModuleModules.add(isWithinRoot ? '/' + relative.split(path.sep).join('/') : file);
|
|
357
|
+
return null;
|
|
358
|
+
},
|
|
359
|
+
|
|
316
360
|
/**
|
|
317
361
|
* Dev SSR middleware. Registered as a pre-hook (no return) so it runs
|
|
318
362
|
* BEFORE Vite's HTML fallback. Config is loaded lazily on first request
|
|
@@ -521,8 +565,7 @@ export function octane(inlineOptions = {}) {
|
|
|
521
565
|
order: 'pre',
|
|
522
566
|
handler(html) {
|
|
523
567
|
if (!isBuild || isSSRBuild || !has_route_config(buildOctaneConfig)) return html;
|
|
524
|
-
|
|
525
|
-
return html.replace('</body>', `${hydrationScript}\n</body>`);
|
|
568
|
+
return injectHydrationEntry(html, VIRTUAL_HYDRATE_ID, HYDRATION_NONCE_PLACEHOLDER);
|
|
526
569
|
},
|
|
527
570
|
},
|
|
528
571
|
|
|
@@ -605,7 +648,14 @@ export function octane(inlineOptions = {}) {
|
|
|
605
648
|
generateServerEntry({
|
|
606
649
|
routes: cfg.router.routes,
|
|
607
650
|
octaneConfigPath: getOctaneConfigPath(root),
|
|
651
|
+
rootBoundary: cfg.rootBoundary,
|
|
652
|
+
rpcModulePaths: [...serverModuleModules],
|
|
608
653
|
clientAssetMap,
|
|
654
|
+
// The virtual entry has no filesystem importer, so resolve app-core
|
|
655
|
+
// from this package before handing source to Vite. This also works
|
|
656
|
+
// when app-core is nested under the plugin by a package manager.
|
|
657
|
+
productionModuleId: requireFromPlugin.resolve('@octanejs/app-core/production'),
|
|
658
|
+
nodeModuleId: requireFromPlugin.resolve('@octanejs/app-core/node'),
|
|
609
659
|
}),
|
|
610
660
|
);
|
|
611
661
|
|
|
@@ -700,8 +750,8 @@ export function octane(inlineOptions = {}) {
|
|
|
700
750
|
// compiler's `.ts`/`.js` hook-slotting pass must skip. Hand-slot-forwarding
|
|
701
751
|
// bindings should not need it: they declare
|
|
702
752
|
// `"octane": { "hookSlots": { "manual": ["src"] } }` in their own package.json and the
|
|
703
|
-
// compiler plugin skips
|
|
704
|
-
//
|
|
753
|
+
// compiler plugin skips those directories via a nearest-manifest lookup.
|
|
754
|
+
// Other installed raw-source Octane packages are transformed automatically.
|
|
705
755
|
const compilerOptions = {};
|
|
706
756
|
if (inlineOptions.hmr !== undefined) compilerOptions.hmr = inlineOptions.hmr;
|
|
707
757
|
if (inlineOptions.exclude !== undefined) compilerOptions.exclude = inlineOptions.exclude;
|
package/src/load-config.js
CHANGED
|
@@ -1,95 +1,62 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
/**
|
|
3
|
-
* Shared utility for loading and resolving octane.config.ts.
|
|
4
|
-
*
|
|
5
|
-
* `resolveOctaneConfig` is the single source of truth for all config
|
|
6
|
-
* validation and default values. Every consumer should receive a
|
|
7
|
-
* `ResolvedOctaneConfig` rather than applying ad-hoc defaults.
|
|
8
|
-
*
|
|
9
|
-
* `loadOctaneConfig` is the single entry point for loading the config
|
|
10
|
-
* file. It accepts an optional Vite dev server — when provided the
|
|
11
|
-
* config is loaded via `ssrLoadModule` (no temp server overhead,
|
|
12
|
-
* HMR-aware). Otherwise a temporary Vite server is spun up, used to
|
|
13
|
-
* transpile the TypeScript config, and immediately shut down.
|
|
14
|
-
*
|
|
15
|
-
* Used by the Vite plugin (during dev + build), the preview CLI script,
|
|
16
|
-
* and the generated production server entry.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/** @import { OctaneConfigOptions, ResolvedOctaneConfig } from '@octanejs/vite-plugin' */
|
|
20
2
|
|
|
21
|
-
import
|
|
22
|
-
|
|
3
|
+
import {
|
|
4
|
+
getOctaneConfigPath,
|
|
5
|
+
loadOctaneConfig as loadCoreOctaneConfig,
|
|
6
|
+
loadOctaneConfigWithMetadata as loadCoreOctaneConfigWithMetadata,
|
|
7
|
+
octaneConfigExists,
|
|
8
|
+
resolveOctaneConfig,
|
|
9
|
+
} from '@octanejs/app-core/config-loader';
|
|
23
10
|
import { compile } from 'octane/compiler';
|
|
24
|
-
import { resolveOctaneConfig } from './resolve-config.js';
|
|
25
11
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// Validation + defaults live in resolve-config.js (a module with no vite /
|
|
29
|
-
// compiler imports) so the production server bundle can include it without
|
|
30
|
-
// dragging the toolchain along. Re-exported here for existing importers.
|
|
31
|
-
export { resolveOctaneConfig } from './resolve-config.js';
|
|
12
|
+
export { getOctaneConfigPath, octaneConfigExists, resolveOctaneConfig };
|
|
32
13
|
|
|
33
14
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
15
|
+
* Vite compatibility facade over app-core's bundler-neutral config loader.
|
|
16
|
+
* A live dev server is adapted to the neutral module-runner contract. Build
|
|
17
|
+
* and preview calls use a temporary Vite module runner so lazy config imports
|
|
18
|
+
* keep Vite's transform semantics without making app-core depend on Vite.
|
|
37
19
|
*
|
|
38
|
-
* @param {string} projectRoot
|
|
39
|
-
* @
|
|
20
|
+
* @param {string} projectRoot
|
|
21
|
+
* @param {{
|
|
22
|
+
* vite?: import('vite').ViteDevServer,
|
|
23
|
+
* moduleRunner?: import('@octanejs/app-core').ConfigModuleRunner | import('@octanejs/app-core').ConfigModuleRunner['loadModule'],
|
|
24
|
+
* requireAdapter?: boolean,
|
|
25
|
+
* configFile?: string,
|
|
26
|
+
* cacheDir?: string,
|
|
27
|
+
* }} [options]
|
|
40
28
|
*/
|
|
41
|
-
export function
|
|
42
|
-
return
|
|
29
|
+
export async function loadOctaneConfig(projectRoot, options = {}) {
|
|
30
|
+
return withDefaultViteRunner(projectRoot, options, loadCoreOctaneConfig);
|
|
43
31
|
}
|
|
44
32
|
|
|
45
33
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
34
|
+
* @param {string} projectRoot
|
|
35
|
+
* @param {{
|
|
36
|
+
* vite?: import('vite').ViteDevServer,
|
|
37
|
+
* moduleRunner?: import('@octanejs/app-core').ConfigModuleRunner | import('@octanejs/app-core').ConfigModuleRunner['loadModule'],
|
|
38
|
+
* requireAdapter?: boolean,
|
|
39
|
+
* configFile?: string,
|
|
40
|
+
* cacheDir?: string,
|
|
41
|
+
* }} [options]
|
|
53
42
|
*/
|
|
54
|
-
export function
|
|
55
|
-
return
|
|
43
|
+
export async function loadOctaneConfigWithMetadata(projectRoot, options = {}) {
|
|
44
|
+
return withDefaultViteRunner(projectRoot, options, loadCoreOctaneConfigWithMetadata);
|
|
56
45
|
}
|
|
57
46
|
|
|
58
47
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* When no dev server is available (build / preview), a temporary Vite server
|
|
66
|
-
* is created in middleware mode, used to transpile the config, then shut down.
|
|
67
|
-
*
|
|
68
|
-
* Throws if the config file does not exist or is invalid.
|
|
69
|
-
*
|
|
70
|
-
* @param {string} projectRoot - Absolute path to the project root
|
|
71
|
-
* @param {{ vite?: import('vite').ViteDevServer, requireAdapter?: boolean }} [options]
|
|
72
|
-
* @returns {Promise<ResolvedOctaneConfig>}
|
|
48
|
+
* @template T
|
|
49
|
+
* @param {string} projectRoot
|
|
50
|
+
* @param {Record<string, any>} options
|
|
51
|
+
* @param {(root: string, options: any) => Promise<T>} loader
|
|
52
|
+
* @returns {Promise<T>}
|
|
73
53
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (!fs.existsSync(configPath)) {
|
|
79
|
-
throw new Error(`[@octanejs/vite-plugin] octane.config.ts not found in ${projectRoot}`);
|
|
54
|
+
async function withDefaultViteRunner(projectRoot, options, loader) {
|
|
55
|
+
if (options.vite || options.moduleRunner) {
|
|
56
|
+
return loader(projectRoot, withViteModuleRunner(options));
|
|
80
57
|
}
|
|
81
58
|
|
|
82
|
-
// When a running Vite dev server is available, use it directly.
|
|
83
|
-
if (vite) {
|
|
84
|
-
const configModule = await vite.ssrLoadModule(configPath);
|
|
85
|
-
return resolveOctaneConfig(configModule.default, { requireAdapter });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Otherwise spin up a temporary Vite server (build / preview).
|
|
89
|
-
// The temp server only transpiles octane.config.ts (plain TypeScript) —
|
|
90
|
-
// no .tsrx compilation plugin is needed beyond config-referenced helpers.
|
|
91
59
|
const { createServer } = await import('vite');
|
|
92
|
-
|
|
93
60
|
const tempVite = await createServer({
|
|
94
61
|
root: projectRoot,
|
|
95
62
|
configFile: false,
|
|
@@ -99,12 +66,9 @@ export async function loadOctaneConfig(projectRoot, options = {}) {
|
|
|
99
66
|
{
|
|
100
67
|
name: 'octane-config-tsrx-loader',
|
|
101
68
|
transform(source, id) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return compile(source,
|
|
105
|
-
mode: 'server',
|
|
106
|
-
hmr: false,
|
|
107
|
-
});
|
|
69
|
+
const file = id.split('?')[0];
|
|
70
|
+
if (!file.endsWith('.tsrx')) return null;
|
|
71
|
+
return compile(source, file, { mode: 'server', hmr: false });
|
|
108
72
|
},
|
|
109
73
|
},
|
|
110
74
|
],
|
|
@@ -112,9 +76,25 @@ export async function loadOctaneConfig(projectRoot, options = {}) {
|
|
|
112
76
|
});
|
|
113
77
|
|
|
114
78
|
try {
|
|
115
|
-
|
|
116
|
-
|
|
79
|
+
return await loader(projectRoot, {
|
|
80
|
+
...options,
|
|
81
|
+
moduleRunner: {
|
|
82
|
+
loadModule: (/** @type {string} */ id) => tempVite.ssrLoadModule(id),
|
|
83
|
+
},
|
|
84
|
+
});
|
|
117
85
|
} finally {
|
|
118
86
|
await tempVite.close();
|
|
119
87
|
}
|
|
120
88
|
}
|
|
89
|
+
|
|
90
|
+
/** @param {Record<string, any>} options */
|
|
91
|
+
function withViteModuleRunner(options) {
|
|
92
|
+
if (!options.vite || options.moduleRunner) return options;
|
|
93
|
+
const { vite, ...rest } = options;
|
|
94
|
+
return {
|
|
95
|
+
...rest,
|
|
96
|
+
moduleRunner: {
|
|
97
|
+
loadModule: (/** @type {string} */ id) => vite.ssrLoadModule(id),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|