@ossido-labs/ossido 0.1.3 → 0.1.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/dist/esm/build/config/normalize-config.js +2 -0
- package/dist/esm/build/config/normalize-config.js.map +1 -1
- package/dist/esm/build/index.js +31 -1
- package/dist/esm/build/index.js.map +1 -1
- package/dist/esm/build/types.d.ts +5 -1
- package/dist/esm/client/index.d.ts +25 -17
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/config/types.d.ts +35 -0
- package/dist/esm/constants.d.ts +6 -0
- package/dist/esm/constants.js +7 -1
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/env/index.d.ts +65 -0
- package/dist/esm/env/index.js +47 -0
- package/dist/esm/env/index.js.map +1 -0
- package/dist/esm/shared/DevResources.js +2 -2
- package/dist/esm/shared/DevResources.js.map +1 -1
- package/dist/esm/shared/OssidoScripts.js +5 -1
- package/dist/esm/shared/OssidoScripts.js.map +1 -1
- package/dist/esm/ssr/server.js +2 -0
- package/dist/esm/ssr/server.js.map +1 -1
- package/dist/esm/types.d.ts +6 -0
- package/package.json +10 -4
|
@@ -66,6 +66,8 @@ const normalizeConfig = (config) => {
|
|
|
66
66
|
ssr: { renderThreads: config.ssr?.renderThreads ?? null },
|
|
67
67
|
output: config.output ?? "server",
|
|
68
68
|
viewTransitions: config.viewTransitions ?? false,
|
|
69
|
+
...config.env !== void 0 ? { env: Array.isArray(config.env) ? config.env : [config.env] } : {},
|
|
70
|
+
...config.lightningcss !== void 0 ? { lightningcss: config.lightningcss } : {},
|
|
69
71
|
...config.build ? { build: config.build } : {}
|
|
70
72
|
};
|
|
71
73
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize-config.js","names":[],"sources":["../../../../src/build/config/normalize-config.ts"],"sourcesContent":["import path from 'node:path';\n\nimport type { AliasOptions } from 'vite';\n\nimport type { OssidoConfig } from '../../config';\n\nimport type { InternalOssidoConfig } from '../types';\n\nimport { DOT_OSSIDO_FOLDER_NAME, CONFIG_FOLDER_NAME } from '../constants';\n\n/**\n * Normalize vite alias option:\n * - If the path starts with `src` folder, transform it to absolute, prepending the ossido root folder\n * - If the path is absolute, remove the \".ossido/config/\" path from it\n * - Otherwise leave the path untouched\n */\nconst normalizeAliasPath = (aliasPath: string): string => {\n if (aliasPath.startsWith('./src') || aliasPath.startsWith('src')) {\n return path.join(process.cwd(), aliasPath);\n }\n\n if (path.isAbsolute(aliasPath)) {\n return aliasPath.replace(\n path.join(DOT_OSSIDO_FOLDER_NAME, CONFIG_FOLDER_NAME),\n '',\n );\n }\n\n return aliasPath;\n};\n\n/**\n * From a given vite aliasOptions apply {@link normalizeAliasPath} for each alias.\n *\n * The config is bundled by `vite` and emitted inside {@link DOT_OSSIDO_FOLDER_NAME}/{@link CONFIG_FOLDER_NAME}.\n * According to this, we have to ensure that the aliases provided by the user are updated to refer to the right folders.\n *\n * @see https://github.com/Valerioageno/tuono/pull/153#issuecomment-2508142877\n */\nconst normalizeViteAlias = (alias?: AliasOptions): AliasOptions | undefined => {\n if (!alias) return;\n\n if (Array.isArray(alias)) {\n return (alias as Extract<AliasOptions, ReadonlyArray<unknown>>).map(\n ({ replacement, ...userAliasDefinition }) => ({\n ...userAliasDefinition,\n replacement: normalizeAliasPath(replacement),\n }),\n );\n }\n\n if (typeof alias === 'object') {\n const normalizedAlias: AliasOptions = {};\n for (const [key, value] of Object.entries(alias)) {\n normalizedAlias[key] = normalizeAliasPath(value as string);\n }\n return normalizedAlias;\n }\n\n return alias;\n};\n\n/**\n * Wrapper function to normalize the ossido.config.ts file\n *\n * @warning Exported for unit test.\n * There is no easy way to mock the module export and change it in every test\n * and also testing the error\n */\nexport const normalizeConfig = (config: OssidoConfig): InternalOssidoConfig => {\n return {\n server: {\n host: config.server?.host ?? 'localhost',\n origin: config.server?.origin ?? null,\n port: config.server?.port ?? 3000,\n },\n vite: {\n alias: normalizeViteAlias(config.vite?.alias),\n css: config.vite?.css,\n optimizeDeps: config.vite?.optimizeDeps,\n plugins: config.vite?.plugins ?? [],\n },\n logging: {\n format: config.logging?.format ?? 'pretty',\n routeTree: config.logging?.routeTree ?? false,\n browser: {\n enabled: config.logging?.browser?.enabled ?? true,\n level: config.logging?.browser?.level ?? 'info',\n },\n },\n dev: {\n criticalCss: config.dev?.criticalCss ?? true,\n },\n ssr: {\n // `null` = auto: the Rust runtime uses the machine's available\n // parallelism (resolving it here would bake in the build machine's cores).\n renderThreads: config.ssr?.renderThreads ?? null,\n },\n output: config.output ?? 'server',\n viewTransitions: config.viewTransitions ?? false,\n // Carried through as-is (its `prebuild`/`postbuild` are functions, so they\n // are only reachable via `loadConfig`, never the JSON config). Spread\n // conditionally so an absent `build` isn't materialised as `undefined`.\n ...(config.build ? { build: config.build } : {}),\n };\n};\n"],"mappings":";;;;;;;;;;AAgBA,MAAM,sBAAsB,cAA8B;CACxD,IAAI,UAAU,WAAW,OAAO,KAAK,UAAU,WAAW,KAAK,GAC7D,OAAO,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS;CAG3C,IAAI,KAAK,WAAW,SAAS,GAC3B,OAAO,UAAU,QACf,KAAK,KAAK,wBAAwB,kBAAkB,GACpD,EACF;CAGF,OAAO;AACT;;;;;;;;;AAUA,MAAM,sBAAsB,UAAmD;CAC7E,IAAI,CAAC,OAAO;CAEZ,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAQ,MAAwD,KAC7D,EAAE,aAAa,GAAG,2BAA2B;EAC5C,GAAG;EACH,aAAa,mBAAmB,WAAW;CAC7C,EACF;CAGF,IAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,kBAAgC,CAAC;EACvC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,gBAAgB,OAAO,mBAAmB,KAAe;EAE3D,OAAO;CACT;CAEA,OAAO;AACT;;;;;;;;AASA,MAAa,mBAAmB,WAA+C;CAC7E,OAAO;EACL,QAAQ;GACN,MAAM,OAAO,QAAQ,QAAQ;GAC7B,QAAQ,OAAO,QAAQ,UAAU;GACjC,MAAM,OAAO,QAAQ,QAAQ;EAC/B;EACA,MAAM;GACJ,OAAO,mBAAmB,OAAO,MAAM,KAAK;GAC5C,KAAK,OAAO,MAAM;GAClB,cAAc,OAAO,MAAM;GAC3B,SAAS,OAAO,MAAM,WAAW,CAAC;EACpC;EACA,SAAS;GACP,QAAQ,OAAO,SAAS,UAAU;GAClC,WAAW,OAAO,SAAS,aAAa;GACxC,SAAS;IACP,SAAS,OAAO,SAAS,SAAS,WAAW;IAC7C,OAAO,OAAO,SAAS,SAAS,SAAS;GAC3C;EACF;EACA,KAAK,EACH,aAAa,OAAO,KAAK,eAAe,KAC1C;EACA,KAAK,EAGH,eAAe,OAAO,KAAK,iBAAiB,KAC9C;EACA,QAAQ,OAAO,UAAU;EACzB,iBAAiB,OAAO,mBAAmB;
|
|
1
|
+
{"version":3,"file":"normalize-config.js","names":[],"sources":["../../../../src/build/config/normalize-config.ts"],"sourcesContent":["import path from 'node:path';\n\nimport type { AliasOptions } from 'vite';\n\nimport type { OssidoConfig } from '../../config';\n\nimport type { InternalOssidoConfig } from '../types';\n\nimport { DOT_OSSIDO_FOLDER_NAME, CONFIG_FOLDER_NAME } from '../constants';\n\n/**\n * Normalize vite alias option:\n * - If the path starts with `src` folder, transform it to absolute, prepending the ossido root folder\n * - If the path is absolute, remove the \".ossido/config/\" path from it\n * - Otherwise leave the path untouched\n */\nconst normalizeAliasPath = (aliasPath: string): string => {\n if (aliasPath.startsWith('./src') || aliasPath.startsWith('src')) {\n return path.join(process.cwd(), aliasPath);\n }\n\n if (path.isAbsolute(aliasPath)) {\n return aliasPath.replace(\n path.join(DOT_OSSIDO_FOLDER_NAME, CONFIG_FOLDER_NAME),\n '',\n );\n }\n\n return aliasPath;\n};\n\n/**\n * From a given vite aliasOptions apply {@link normalizeAliasPath} for each alias.\n *\n * The config is bundled by `vite` and emitted inside {@link DOT_OSSIDO_FOLDER_NAME}/{@link CONFIG_FOLDER_NAME}.\n * According to this, we have to ensure that the aliases provided by the user are updated to refer to the right folders.\n *\n * @see https://github.com/Valerioageno/tuono/pull/153#issuecomment-2508142877\n */\nconst normalizeViteAlias = (alias?: AliasOptions): AliasOptions | undefined => {\n if (!alias) return;\n\n if (Array.isArray(alias)) {\n return (alias as Extract<AliasOptions, ReadonlyArray<unknown>>).map(\n ({ replacement, ...userAliasDefinition }) => ({\n ...userAliasDefinition,\n replacement: normalizeAliasPath(replacement),\n }),\n );\n }\n\n if (typeof alias === 'object') {\n const normalizedAlias: AliasOptions = {};\n for (const [key, value] of Object.entries(alias)) {\n normalizedAlias[key] = normalizeAliasPath(value as string);\n }\n return normalizedAlias;\n }\n\n return alias;\n};\n\n/**\n * Wrapper function to normalize the ossido.config.ts file\n *\n * @warning Exported for unit test.\n * There is no easy way to mock the module export and change it in every test\n * and also testing the error\n */\nexport const normalizeConfig = (config: OssidoConfig): InternalOssidoConfig => {\n return {\n server: {\n host: config.server?.host ?? 'localhost',\n origin: config.server?.origin ?? null,\n port: config.server?.port ?? 3000,\n },\n vite: {\n alias: normalizeViteAlias(config.vite?.alias),\n css: config.vite?.css,\n optimizeDeps: config.vite?.optimizeDeps,\n plugins: config.vite?.plugins ?? [],\n },\n logging: {\n format: config.logging?.format ?? 'pretty',\n routeTree: config.logging?.routeTree ?? false,\n browser: {\n enabled: config.logging?.browser?.enabled ?? true,\n level: config.logging?.browser?.level ?? 'info',\n },\n },\n dev: {\n criticalCss: config.dev?.criticalCss ?? true,\n },\n ssr: {\n // `null` = auto: the Rust runtime uses the machine's available\n // parallelism (resolving it here would bake in the build machine's cores).\n renderThreads: config.ssr?.renderThreads ?? null,\n },\n output: config.output ?? 'server',\n viewTransitions: config.viewTransitions ?? false,\n // Coerce a single path to a one-element array so the Rust side always sees\n // `Option<Vec<String>>`. Absent stays absent (default cascade).\n ...(config.env !== undefined\n ? { env: Array.isArray(config.env) ? config.env : [config.env] }\n : {}),\n // Passed through untouched; interpreted by the Vite config (JS-only).\n ...(config.lightningcss !== undefined\n ? { lightningcss: config.lightningcss }\n : {}),\n // Carried through as-is (its `prebuild`/`postbuild` are functions, so they\n // are only reachable via `loadConfig`, never the JSON config). Spread\n // conditionally so an absent `build` isn't materialised as `undefined`.\n ...(config.build ? { build: config.build } : {}),\n };\n};\n"],"mappings":";;;;;;;;;;AAgBA,MAAM,sBAAsB,cAA8B;CACxD,IAAI,UAAU,WAAW,OAAO,KAAK,UAAU,WAAW,KAAK,GAC7D,OAAO,KAAK,KAAK,QAAQ,IAAI,GAAG,SAAS;CAG3C,IAAI,KAAK,WAAW,SAAS,GAC3B,OAAO,UAAU,QACf,KAAK,KAAK,wBAAwB,kBAAkB,GACpD,EACF;CAGF,OAAO;AACT;;;;;;;;;AAUA,MAAM,sBAAsB,UAAmD;CAC7E,IAAI,CAAC,OAAO;CAEZ,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAQ,MAAwD,KAC7D,EAAE,aAAa,GAAG,2BAA2B;EAC5C,GAAG;EACH,aAAa,mBAAmB,WAAW;CAC7C,EACF;CAGF,IAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,kBAAgC,CAAC;EACvC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,gBAAgB,OAAO,mBAAmB,KAAe;EAE3D,OAAO;CACT;CAEA,OAAO;AACT;;;;;;;;AASA,MAAa,mBAAmB,WAA+C;CAC7E,OAAO;EACL,QAAQ;GACN,MAAM,OAAO,QAAQ,QAAQ;GAC7B,QAAQ,OAAO,QAAQ,UAAU;GACjC,MAAM,OAAO,QAAQ,QAAQ;EAC/B;EACA,MAAM;GACJ,OAAO,mBAAmB,OAAO,MAAM,KAAK;GAC5C,KAAK,OAAO,MAAM;GAClB,cAAc,OAAO,MAAM;GAC3B,SAAS,OAAO,MAAM,WAAW,CAAC;EACpC;EACA,SAAS;GACP,QAAQ,OAAO,SAAS,UAAU;GAClC,WAAW,OAAO,SAAS,aAAa;GACxC,SAAS;IACP,SAAS,OAAO,SAAS,SAAS,WAAW;IAC7C,OAAO,OAAO,SAAS,SAAS,SAAS;GAC3C;EACF;EACA,KAAK,EACH,aAAa,OAAO,KAAK,eAAe,KAC1C;EACA,KAAK,EAGH,eAAe,OAAO,KAAK,iBAAiB,KAC9C;EACA,QAAQ,OAAO,UAAU;EACzB,iBAAiB,OAAO,mBAAmB;EAG3C,GAAI,OAAO,QAAQ,SACf,EAAE,KAAK,MAAM,QAAQ,OAAO,GAAG,IAAI,OAAO,MAAM,CAAC,OAAO,GAAG,EAAE,IAC7D,CAAC;EAEL,GAAI,OAAO,iBAAiB,SACxB,EAAE,cAAc,OAAO,aAAa,IACpC,CAAC;EAIL,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;CAChD;AACF"}
|
package/dist/esm/build/index.js
CHANGED
|
@@ -10,6 +10,8 @@ import { gzipSync } from "node:zlib";
|
|
|
10
10
|
import { build, createServer, mergeConfig } from "vite";
|
|
11
11
|
import react from "@vitejs/plugin-react-swc";
|
|
12
12
|
import inject from "@rollup/plugin-inject";
|
|
13
|
+
import browserslist from "browserslist";
|
|
14
|
+
import { browserslistToTargets } from "lightningcss";
|
|
13
15
|
import { OssidoReactPlugin, routeGenerator } from "@ossido-labs/ossido-react-vite-plugin";
|
|
14
16
|
import { ErrorOverlayVitePlugin } from "@ossido-labs/ossido-ui/vite-plugin";
|
|
15
17
|
|
|
@@ -50,6 +52,32 @@ const VITE_SSR_PLUGINS = [{
|
|
|
50
52
|
Event: [SSR_POLYFILLS_MODULE, "EventPolyfill"]
|
|
51
53
|
})
|
|
52
54
|
}];
|
|
55
|
+
/** Default browser-targets query when `lightningcss` is enabled without one. */
|
|
56
|
+
const DEFAULT_BROWSERSLIST = "defaults";
|
|
57
|
+
/**
|
|
58
|
+
* Vite `css` / `build.cssMinify` fragments for the (optional) Lightning CSS
|
|
59
|
+
* pipeline. Returns `{}` when disabled, so the default Vite/PostCSS behaviour is
|
|
60
|
+
* untouched. When enabled, it sets `css.transformer: 'lightningcss'` (applies in
|
|
61
|
+
* dev-serve and build) and `cssMinify: 'lightningcss'` (prod build), deriving
|
|
62
|
+
* targets from a browserslist query (`targets` override, else `'defaults'`).
|
|
63
|
+
*/
|
|
64
|
+
function lightningCssOverrides(ossidoConfig) {
|
|
65
|
+
const option = ossidoConfig.lightningcss;
|
|
66
|
+
if (!option) return {};
|
|
67
|
+
const query = typeof option === "object" ? option.targets : void 0;
|
|
68
|
+
const targets = browserslistToTargets(browserslist(query ?? DEFAULT_BROWSERSLIST));
|
|
69
|
+
return {
|
|
70
|
+
css: {
|
|
71
|
+
...ossidoConfig.vite?.css,
|
|
72
|
+
transformer: "lightningcss",
|
|
73
|
+
lightningcss: {
|
|
74
|
+
...ossidoConfig.vite?.css?.lightningcss,
|
|
75
|
+
targets
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
cssMinify: "lightningcss"
|
|
79
|
+
};
|
|
80
|
+
}
|
|
53
81
|
/**
|
|
54
82
|
* From a resolved {@link InternalOssidoConfig} return a `vite` "mergeable"
|
|
55
83
|
* {@link InlineConfig} including all default ossido related options
|
|
@@ -60,6 +88,7 @@ function createBaseViteConfigFromOssidoConfig(ossidoConfig) {
|
|
|
60
88
|
* packages/lazy-fn-vite-plugin/tests/transpileSource.test.ts
|
|
61
89
|
*/
|
|
62
90
|
const pluginFilesInclude = /\.(jsx|js|mdx|md|tsx|ts)$/;
|
|
91
|
+
const lightning = lightningCssOverrides(ossidoConfig);
|
|
63
92
|
return {
|
|
64
93
|
root: ".ossido",
|
|
65
94
|
logLevel: "error",
|
|
@@ -74,7 +103,8 @@ function createBaseViteConfigFromOssidoConfig(ossidoConfig) {
|
|
|
74
103
|
__OSSIDO_VIEW_TRANSITIONS__: JSON.stringify(ossidoConfig.viewTransitions ?? false)
|
|
75
104
|
},
|
|
76
105
|
resolve: { alias: ossidoConfig.vite?.alias ?? {} },
|
|
77
|
-
css: ossidoConfig.vite?.css,
|
|
106
|
+
css: lightning.css ?? ossidoConfig.vite?.css,
|
|
107
|
+
...lightning.cssMinify ? { build: { cssMinify: lightning.cssMinify } } : {},
|
|
78
108
|
optimizeDeps: ossidoConfig.vite?.optimizeDeps,
|
|
79
109
|
plugins: [
|
|
80
110
|
...ossidoConfig.vite?.plugins ?? [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/build/index.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { resolve, join } from 'node:path';\nimport { readdirSync, readFileSync } from 'node:fs';\nimport { gzipSync } from 'node:zlib';\n\nimport type { InlineConfig, Plugin } from 'vite';\nimport { build, createServer, mergeConfig } from 'vite';\nimport react from '@vitejs/plugin-react-swc';\nimport inject from '@rollup/plugin-inject';\nimport {\n OssidoReactPlugin,\n routeGenerator,\n} from '@ossido-labs/ossido-react-vite-plugin';\nimport { ErrorOverlayVitePlugin } from '@ossido-labs/ossido-ui/vite-plugin';\n\nimport type { OssidoConfig } from '../config';\n\nimport type { InternalOssidoConfig } from './types';\nimport { blockingAsync, listFilesRecursive } from './utils';\nimport { createJsonConfig, loadConfig } from './config';\nimport { ENV_PREFIX } from './constants';\nimport { createOssidoViteLogger } from './logger';\n\nconst require = createRequire(import.meta.url);\n\n/**\n * Absolute entry paths for the generated `.ossido` scaffold.\n *\n * The vite config sets `root: '.ossido'`. Vite's `build()` resolves a relative\n * `rollupOptions.input` against the CWD, but the dev server's dependency\n * scanner (rolldown, vite 8+) resolves it against `root` — so a path like\n * `./.ossido/client-main.tsx` double-nests to `.ossido/.ossido/...` and fails to\n * resolve. Absolute paths are unambiguous across both code paths.\n */\nconst CLIENT_MAIN_ENTRY = resolve('.ossido/client-main.tsx');\nconst SERVER_MAIN_ENTRY = resolve('.ossido/server-main.tsx');\n\n/**\n * `@rollup/plugin-inject` injects these imports into every module that\n * references the globals below — including third-party files deep in\n * `node_modules` (e.g. `react-dom/server`). Vite 8's bundler (rolldown)\n * resolves an injected bare specifier relative to the *importing* file, so\n * `'@ossido-labs/ossido/ssr'` fails to resolve from within `react-dom`. Resolving to\n * absolute paths up-front makes the injected imports resolvable from anywhere\n * (and is a no-op for rollup on Vite <= 7).\n */\nconst SSR_POLYFILLS_MODULE = require.resolve('@ossido-labs/ossido/ssr');\nconst WEB_STREAMS_POLYFILL_MODULE = require.resolve('web-streams-polyfill');\n\nconst VITE_SSR_PLUGINS: Array<Plugin> = [\n {\n enforce: 'post',\n // `@rollup/plugin-inject` is typed against an older rollup than the one\n // bundled with vite, so its `Plugin` type needs widening to vite's.\n ...(inject({\n ReadableStream: [WEB_STREAMS_POLYFILL_MODULE, 'ReadableStream'],\n\n /**\n * Added to support `react@19`\n * @see https://github.com/tuono-labs/tuono/issues/218\n */\n MessageChannel: [SSR_POLYFILLS_MODULE, 'MessageChannelPolyfill'],\n MessageEvent: [SSR_POLYFILLS_MODULE, 'MessageEventPolyfill'],\n Event: [SSR_POLYFILLS_MODULE, 'EventPolyfill'],\n }) as unknown as Plugin),\n },\n];\n\n/**\n * From a resolved {@link InternalOssidoConfig} return a `vite` \"mergeable\"\n * {@link InlineConfig} including all default ossido related options\n */\nfunction createBaseViteConfigFromOssidoConfig(\n ossidoConfig: InternalOssidoConfig,\n): InlineConfig {\n /**\n * @warning Keep in sync with {@link LazyLoadingPlugin} tests:\n * packages/lazy-fn-vite-plugin/tests/transpileSource.test.ts\n */\n const pluginFilesInclude = /\\.(jsx|js|mdx|md|tsx|ts)$/;\n\n const viteBaseConfig: InlineConfig = {\n root: '.ossido',\n // `'error'` (not `'silent'`) so build failures surface instead of\n // degrading to a silent client-side fallback with hydration mismatches.\n logLevel: 'error',\n // Tag vite's output `[FE]` so it matches the rest of the Ossido logs. The\n // level is passed explicitly so the logger honours it (see the logger docs)\n // — this keeps vite's info/benign-warning noise out at `'error'`.\n customLogger: createOssidoViteLogger('error'),\n publicDir: '../public',\n cacheDir: 'cache',\n envDir: '../',\n envPrefix: ENV_PREFIX,\n\n define: {\n // Baked into the client bundle for `ossido build --static` (the CLI sets\n // `OSSIDO_STATIC` for the react build). The router reads it to fetch the\n // pre-rendered `.json` data files instead of the extensionless\n // `/__ossido/data{path}` route, which has no server in a static export.\n __OSSIDO_STATIC__: JSON.stringify(process.env.OSSIDO_STATIC === 'true'),\n // Baked into both bundles so `<CriticalCss>` skips rendering its dev\n // stylesheet links when `dev.criticalCss: false` — the vite endpoint is\n // disabled in that case, so the links would otherwise be dead requests\n // that block rendering.\n __OSSIDO_CRITICAL_CSS__: JSON.stringify(\n ossidoConfig.dev?.criticalCss ?? true,\n ),\n // Baked into both bundles so the router wraps client navigations in\n // `document.startViewTransition` only when the app opted in via\n // `viewTransitions: true`. Off by default.\n __OSSIDO_VIEW_TRANSITIONS__: JSON.stringify(\n ossidoConfig.viewTransitions ?? false,\n ),\n },\n\n resolve: {\n alias: ossidoConfig.vite?.alias ?? {},\n },\n\n css: ossidoConfig.vite?.css,\n\n optimizeDeps: ossidoConfig.vite?.optimizeDeps,\n\n plugins: [\n ...(ossidoConfig.vite?.plugins ?? []),\n\n /**\n * even if `include` is not a valid option for this\n * plugin, we have to use it.\n * If not specified, when running `ossido dev`, the mdx\n * won't be compiled include any style in the page and it might\n * seem broken.\n */\n // @ts-expect-error see above comment\n react({ include: pluginFilesInclude }),\n\n OssidoReactPlugin({ criticalCss: ossidoConfig.dev?.criticalCss ?? true }),\n ],\n };\n\n // seems redundant but it's useful to log the value when debugging, until we have a logging infrastructure.\n return viteBaseConfig;\n}\n\nconst developmentSSRBundle = (): void => {\n blockingAsync(async () => {\n const config = await loadConfig();\n await build(\n mergeConfig<InlineConfig, InlineConfig>(\n createBaseViteConfigFromOssidoConfig(config),\n {\n // A dedicated cache dir, separate from the client dev server's\n // (`.ossido/cache`). Both run concurrently in dev, and this SSR bundle\n // is rebuilt on every `.tsx`/`.mdx`/CSS-module edit — sharing the cache\n // makes each rebuild invalidate the dev server's optimized-deps cache,\n // which triggers a spurious full-page reload (and data refetch) right\n // after the in-place Fast Refresh has already applied.\n cacheDir: 'cache-ssr',\n plugins: VITE_SSR_PLUGINS,\n build: {\n ssr: true,\n minify: false,\n outDir: 'server',\n emptyOutDir: true,\n rollupOptions: {\n input: SERVER_MAIN_ENTRY,\n output: {\n entryFileNames: 'dev-server.js',\n // ESM (not IIFE) so the SSR entry can use top-level `await`; the\n // Rust runtime compiles it as a V8 module (`Ssr::from_module`).\n // The build stays a single `dev-server.js` — the only file the\n // runtime reads — because the SSR build disables code splitting.\n format: 'es',\n },\n },\n },\n ssr: {\n target: 'webworker',\n noExternal: true,\n },\n },\n ),\n );\n });\n};\n\nconst developmentCSRWatch = (): void => {\n blockingAsync(async () => {\n const config = await loadConfig();\n\n const server = await createServer(\n mergeConfig<InlineConfig, InlineConfig>(\n createBaseViteConfigFromOssidoConfig(config),\n {\n // Entry point for the development vite proxy\n base: '/vite-server/',\n plugins: [ErrorOverlayVitePlugin],\n\n server: {\n host: config.server.host,\n port: config.server.port + 1,\n strictPort: true,\n },\n build: {\n manifest: true,\n emptyOutDir: true,\n rollupOptions: {\n input: CLIENT_MAIN_ENTRY,\n },\n },\n },\n ),\n );\n await server.listen();\n });\n};\n\nconst KB = 1024;\n\nconst formatKb = (bytes: number): string => `${(bytes / KB).toFixed(1)} kB`;\n\n/**\n * Print a compact per-asset size summary (raw + gzip) for the prod build, so\n * size regressions are visible on every `ossido build` without extra tooling.\n * Reads the emitted files directly — works regardless of vite's log level.\n */\nconst printBundleSummary = (): void => {\n interface AssetRow {\n label: string;\n size: number;\n gzip: number;\n }\n\n const collectDir = (dir: string, prefix: string): Array<AssetRow> => {\n try {\n return readdirSync(dir, { recursive: true, encoding: 'utf-8' })\n .filter((file) => /\\.(js|css)$/.test(file))\n .map((file) => {\n const content = readFileSync(join(dir, file));\n return {\n label: `${prefix}/${file}`,\n size: content.length,\n gzip: gzipSync(content).length,\n };\n });\n } catch {\n return [];\n }\n };\n\n const assets = [\n ...collectDir(resolve('out/client'), 'client'),\n ...collectDir(resolve('out/server'), 'server'),\n ].sort((a, b) => b.size - a.size);\n\n if (!assets.length) return;\n\n const labelWidth = Math.max(...assets.map((asset) => asset.label.length));\n console.log('');\n for (const asset of assets) {\n console.log(\n ` ${asset.label.padEnd(labelWidth)} ${formatKb(asset.size).padStart(10)} │ gzip: ${formatKb(asset.gzip).padStart(9)}`,\n );\n }\n const total = assets.reduce((sum, asset) => sum + asset.size, 0);\n const totalGzip = assets.reduce((sum, asset) => sum + asset.gzip, 0);\n console.log(\n ` ${'total'.padEnd(labelWidth)} ${formatKb(total).padStart(10)} │ gzip: ${formatKb(totalGzip).padStart(9)}`,\n );\n console.log('');\n};\n\nconst buildProd = (): void => {\n blockingAsync(async () => {\n const config = await loadConfig();\n\n // Generate the route tree once up front. Both builds' plugin instances\n // re-run the generator on `configResolved`, but it skips the write when\n // the content is unchanged — pre-generating removes the concurrent-write\n // hazard so the two builds (independent outputs) can run in parallel.\n await routeGenerator();\n\n await Promise.all([\n build(\n mergeConfig<InlineConfig, InlineConfig>(\n createBaseViteConfigFromOssidoConfig(config),\n {\n build: {\n manifest: true,\n emptyOutDir: true,\n outDir: '../out/client',\n rollupOptions: {\n input: CLIENT_MAIN_ENTRY,\n },\n },\n },\n ),\n ),\n build(\n mergeConfig<InlineConfig, InlineConfig>(\n createBaseViteConfigFromOssidoConfig(config),\n {\n plugins: VITE_SSR_PLUGINS,\n build: {\n ssr: true,\n minify: true,\n outDir: '../out/server',\n emptyOutDir: true,\n rollupOptions: {\n input: SERVER_MAIN_ENTRY,\n output: {\n entryFileNames: 'prod-server.js',\n // ESM (not IIFE) so the SSR entry can use top-level `await`;\n // the Rust runtime compiles it as a V8 module\n // (`Ssr::from_module`). The build stays a single\n // `prod-server.js` — the only file the runtime reads —\n // because the SSR build disables code splitting.\n format: 'es',\n },\n },\n },\n ssr: {\n target: 'webworker',\n noExternal: true,\n },\n },\n ),\n ),\n ]);\n\n printBundleSummary();\n });\n};\n\nconst buildConfig = (): void => {\n blockingAsync(async (): Promise<void> => {\n await build({\n root: '.ossido',\n // `'error'` (not `'silent'`) so config build failures are visible.\n logLevel: 'error',\n cacheDir: 'cache',\n envDir: '../',\n build: {\n ssr: true,\n outDir: 'config',\n emptyOutDir: true,\n rollupOptions: {\n input: './ossido.config.ts',\n output: {\n entryFileNames: 'config.mjs',\n },\n },\n },\n });\n\n const config = await loadConfig();\n await createJsonConfig(config);\n });\n};\n\n/**\n * Run a user-defined build lifecycle hook (`prebuild` / `postbuild`) from\n * `ossido.config.ts`. Invoked by the Rust CLI (`ossido-run-build-hook <name>`)\n * at the matching point of `ossido build`. No-ops when the hook is undefined.\n *\n * The output/public dirs and mode mirror what the build produces: static builds\n * deploy `out/static`, server builds `out`. `postbuild` gets the emitted-file\n * manifest; `prebuild` runs before anything is emitted, so its manifest is empty.\n */\nconst runBuildHook = (name: 'prebuild' | 'postbuild'): void =>\n blockingAsync(async () => {\n const config = await loadConfig();\n const hook = config.build?.[name];\n if (typeof hook !== 'function') return;\n\n const mode = process.env.OSSIDO_STATIC === 'true' ? 'static' : 'server';\n const outputDirectory = resolve(mode === 'static' ? 'out/static' : 'out');\n const publicDirectory = resolve('public');\n const manifest =\n name === 'postbuild' ? listFilesRecursive(outputDirectory) : [];\n\n // `loadConfig` returns the resolved `InternalOssidoConfig` (a structural\n // superset of the public `OssidoConfig` the hook context exposes).\n await hook({\n mode,\n outputDirectory,\n publicDirectory,\n manifest,\n config: config as OssidoConfig,\n });\n });\n\nexport {\n buildProd,\n buildConfig,\n developmentCSRWatch,\n developmentSSRBundle,\n runBuildHook,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/build/index.ts"],"sourcesContent":["import { createRequire } from 'node:module';\nimport { resolve, join } from 'node:path';\nimport { readdirSync, readFileSync } from 'node:fs';\nimport { gzipSync } from 'node:zlib';\n\nimport type { InlineConfig, Plugin } from 'vite';\nimport { build, createServer, mergeConfig } from 'vite';\nimport react from '@vitejs/plugin-react-swc';\nimport inject from '@rollup/plugin-inject';\nimport browserslist from 'browserslist';\nimport { browserslistToTargets } from 'lightningcss';\nimport {\n OssidoReactPlugin,\n routeGenerator,\n} from '@ossido-labs/ossido-react-vite-plugin';\nimport { ErrorOverlayVitePlugin } from '@ossido-labs/ossido-ui/vite-plugin';\n\nimport type { OssidoConfig } from '../config';\n\nimport type { InternalOssidoConfig } from './types';\nimport { blockingAsync, listFilesRecursive } from './utils';\nimport { createJsonConfig, loadConfig } from './config';\nimport { ENV_PREFIX } from './constants';\nimport { createOssidoViteLogger } from './logger';\n\nconst require = createRequire(import.meta.url);\n\n/**\n * Absolute entry paths for the generated `.ossido` scaffold.\n *\n * The vite config sets `root: '.ossido'`. Vite's `build()` resolves a relative\n * `rollupOptions.input` against the CWD, but the dev server's dependency\n * scanner (rolldown, vite 8+) resolves it against `root` — so a path like\n * `./.ossido/client-main.tsx` double-nests to `.ossido/.ossido/...` and fails to\n * resolve. Absolute paths are unambiguous across both code paths.\n */\nconst CLIENT_MAIN_ENTRY = resolve('.ossido/client-main.tsx');\nconst SERVER_MAIN_ENTRY = resolve('.ossido/server-main.tsx');\n\n/**\n * `@rollup/plugin-inject` injects these imports into every module that\n * references the globals below — including third-party files deep in\n * `node_modules` (e.g. `react-dom/server`). Vite 8's bundler (rolldown)\n * resolves an injected bare specifier relative to the *importing* file, so\n * `'@ossido-labs/ossido/ssr'` fails to resolve from within `react-dom`. Resolving to\n * absolute paths up-front makes the injected imports resolvable from anywhere\n * (and is a no-op for rollup on Vite <= 7).\n */\nconst SSR_POLYFILLS_MODULE = require.resolve('@ossido-labs/ossido/ssr');\nconst WEB_STREAMS_POLYFILL_MODULE = require.resolve('web-streams-polyfill');\n\nconst VITE_SSR_PLUGINS: Array<Plugin> = [\n {\n enforce: 'post',\n // `@rollup/plugin-inject` is typed against an older rollup than the one\n // bundled with vite, so its `Plugin` type needs widening to vite's.\n ...(inject({\n ReadableStream: [WEB_STREAMS_POLYFILL_MODULE, 'ReadableStream'],\n\n /**\n * Added to support `react@19`\n * @see https://github.com/tuono-labs/tuono/issues/218\n */\n MessageChannel: [SSR_POLYFILLS_MODULE, 'MessageChannelPolyfill'],\n MessageEvent: [SSR_POLYFILLS_MODULE, 'MessageEventPolyfill'],\n Event: [SSR_POLYFILLS_MODULE, 'EventPolyfill'],\n }) as unknown as Plugin),\n },\n];\n\n/** Default browser-targets query when `lightningcss` is enabled without one. */\nconst DEFAULT_BROWSERSLIST = 'defaults';\n\n/**\n * Vite `css` / `build.cssMinify` fragments for the (optional) Lightning CSS\n * pipeline. Returns `{}` when disabled, so the default Vite/PostCSS behaviour is\n * untouched. When enabled, it sets `css.transformer: 'lightningcss'` (applies in\n * dev-serve and build) and `cssMinify: 'lightningcss'` (prod build), deriving\n * targets from a browserslist query (`targets` override, else `'defaults'`).\n */\nfunction lightningCssOverrides(ossidoConfig: InternalOssidoConfig): {\n css?: InlineConfig['css'];\n cssMinify?: 'lightningcss';\n} {\n const option = ossidoConfig.lightningcss;\n if (!option) return {};\n\n const query = typeof option === 'object' ? option.targets : undefined;\n const targets = browserslistToTargets(\n browserslist(query ?? DEFAULT_BROWSERSLIST),\n );\n\n return {\n css: {\n ...ossidoConfig.vite?.css,\n transformer: 'lightningcss',\n lightningcss: { ...ossidoConfig.vite?.css?.lightningcss, targets },\n },\n cssMinify: 'lightningcss',\n };\n}\n\n/**\n * From a resolved {@link InternalOssidoConfig} return a `vite` \"mergeable\"\n * {@link InlineConfig} including all default ossido related options\n */\nfunction createBaseViteConfigFromOssidoConfig(\n ossidoConfig: InternalOssidoConfig,\n): InlineConfig {\n /**\n * @warning Keep in sync with {@link LazyLoadingPlugin} tests:\n * packages/lazy-fn-vite-plugin/tests/transpileSource.test.ts\n */\n const pluginFilesInclude = /\\.(jsx|js|mdx|md|tsx|ts)$/;\n\n // Optional Lightning CSS pipeline (empty unless `lightningcss` is configured).\n const lightning = lightningCssOverrides(ossidoConfig);\n\n const viteBaseConfig: InlineConfig = {\n root: '.ossido',\n // `'error'` (not `'silent'`) so build failures surface instead of\n // degrading to a silent client-side fallback with hydration mismatches.\n logLevel: 'error',\n // Tag vite's output `[FE]` so it matches the rest of the Ossido logs. The\n // level is passed explicitly so the logger honours it (see the logger docs)\n // — this keeps vite's info/benign-warning noise out at `'error'`.\n customLogger: createOssidoViteLogger('error'),\n publicDir: '../public',\n cacheDir: 'cache',\n envDir: '../',\n envPrefix: ENV_PREFIX,\n\n define: {\n // Baked into the client bundle for `ossido build --static` (the CLI sets\n // `OSSIDO_STATIC` for the react build). The router reads it to fetch the\n // pre-rendered `.json` data files instead of the extensionless\n // `/__ossido/data{path}` route, which has no server in a static export.\n __OSSIDO_STATIC__: JSON.stringify(process.env.OSSIDO_STATIC === 'true'),\n // Baked into both bundles so `<CriticalCss>` skips rendering its dev\n // stylesheet links when `dev.criticalCss: false` — the vite endpoint is\n // disabled in that case, so the links would otherwise be dead requests\n // that block rendering.\n __OSSIDO_CRITICAL_CSS__: JSON.stringify(\n ossidoConfig.dev?.criticalCss ?? true,\n ),\n // Baked into both bundles so the router wraps client navigations in\n // `document.startViewTransition` only when the app opted in via\n // `viewTransitions: true`. Off by default.\n __OSSIDO_VIEW_TRANSITIONS__: JSON.stringify(\n ossidoConfig.viewTransitions ?? false,\n ),\n },\n\n resolve: {\n alias: ossidoConfig.vite?.alias ?? {},\n },\n\n css: lightning.css ?? ossidoConfig.vite?.css,\n\n // Only present when Lightning CSS is enabled; merged into every build phase\n // (a no-op for the dev CSR serve, which doesn't minify).\n ...(lightning.cssMinify\n ? { build: { cssMinify: lightning.cssMinify } }\n : {}),\n\n optimizeDeps: ossidoConfig.vite?.optimizeDeps,\n\n plugins: [\n ...(ossidoConfig.vite?.plugins ?? []),\n\n /**\n * even if `include` is not a valid option for this\n * plugin, we have to use it.\n * If not specified, when running `ossido dev`, the mdx\n * won't be compiled include any style in the page and it might\n * seem broken.\n */\n // @ts-expect-error see above comment\n react({ include: pluginFilesInclude }),\n\n OssidoReactPlugin({ criticalCss: ossidoConfig.dev?.criticalCss ?? true }),\n ],\n };\n\n // seems redundant but it's useful to log the value when debugging, until we have a logging infrastructure.\n return viteBaseConfig;\n}\n\nconst developmentSSRBundle = (): void => {\n blockingAsync(async () => {\n const config = await loadConfig();\n await build(\n mergeConfig<InlineConfig, InlineConfig>(\n createBaseViteConfigFromOssidoConfig(config),\n {\n // A dedicated cache dir, separate from the client dev server's\n // (`.ossido/cache`). Both run concurrently in dev, and this SSR bundle\n // is rebuilt on every `.tsx`/`.mdx`/CSS-module edit — sharing the cache\n // makes each rebuild invalidate the dev server's optimized-deps cache,\n // which triggers a spurious full-page reload (and data refetch) right\n // after the in-place Fast Refresh has already applied.\n cacheDir: 'cache-ssr',\n plugins: VITE_SSR_PLUGINS,\n build: {\n ssr: true,\n minify: false,\n outDir: 'server',\n emptyOutDir: true,\n rollupOptions: {\n input: SERVER_MAIN_ENTRY,\n output: {\n entryFileNames: 'dev-server.js',\n // ESM (not IIFE) so the SSR entry can use top-level `await`; the\n // Rust runtime compiles it as a V8 module (`Ssr::from_module`).\n // The build stays a single `dev-server.js` — the only file the\n // runtime reads — because the SSR build disables code splitting.\n format: 'es',\n },\n },\n },\n ssr: {\n target: 'webworker',\n noExternal: true,\n },\n },\n ),\n );\n });\n};\n\nconst developmentCSRWatch = (): void => {\n blockingAsync(async () => {\n const config = await loadConfig();\n\n const server = await createServer(\n mergeConfig<InlineConfig, InlineConfig>(\n createBaseViteConfigFromOssidoConfig(config),\n {\n // Entry point for the development vite proxy\n base: '/vite-server/',\n plugins: [ErrorOverlayVitePlugin],\n\n server: {\n host: config.server.host,\n port: config.server.port + 1,\n strictPort: true,\n },\n build: {\n manifest: true,\n emptyOutDir: true,\n rollupOptions: {\n input: CLIENT_MAIN_ENTRY,\n },\n },\n },\n ),\n );\n await server.listen();\n });\n};\n\nconst KB = 1024;\n\nconst formatKb = (bytes: number): string => `${(bytes / KB).toFixed(1)} kB`;\n\n/**\n * Print a compact per-asset size summary (raw + gzip) for the prod build, so\n * size regressions are visible on every `ossido build` without extra tooling.\n * Reads the emitted files directly — works regardless of vite's log level.\n */\nconst printBundleSummary = (): void => {\n interface AssetRow {\n label: string;\n size: number;\n gzip: number;\n }\n\n const collectDir = (dir: string, prefix: string): Array<AssetRow> => {\n try {\n return readdirSync(dir, { recursive: true, encoding: 'utf-8' })\n .filter((file) => /\\.(js|css)$/.test(file))\n .map((file) => {\n const content = readFileSync(join(dir, file));\n return {\n label: `${prefix}/${file}`,\n size: content.length,\n gzip: gzipSync(content).length,\n };\n });\n } catch {\n return [];\n }\n };\n\n const assets = [\n ...collectDir(resolve('out/client'), 'client'),\n ...collectDir(resolve('out/server'), 'server'),\n ].sort((a, b) => b.size - a.size);\n\n if (!assets.length) return;\n\n const labelWidth = Math.max(...assets.map((asset) => asset.label.length));\n console.log('');\n for (const asset of assets) {\n console.log(\n ` ${asset.label.padEnd(labelWidth)} ${formatKb(asset.size).padStart(10)} │ gzip: ${formatKb(asset.gzip).padStart(9)}`,\n );\n }\n const total = assets.reduce((sum, asset) => sum + asset.size, 0);\n const totalGzip = assets.reduce((sum, asset) => sum + asset.gzip, 0);\n console.log(\n ` ${'total'.padEnd(labelWidth)} ${formatKb(total).padStart(10)} │ gzip: ${formatKb(totalGzip).padStart(9)}`,\n );\n console.log('');\n};\n\nconst buildProd = (): void => {\n blockingAsync(async () => {\n const config = await loadConfig();\n\n // Generate the route tree once up front. Both builds' plugin instances\n // re-run the generator on `configResolved`, but it skips the write when\n // the content is unchanged — pre-generating removes the concurrent-write\n // hazard so the two builds (independent outputs) can run in parallel.\n await routeGenerator();\n\n await Promise.all([\n build(\n mergeConfig<InlineConfig, InlineConfig>(\n createBaseViteConfigFromOssidoConfig(config),\n {\n build: {\n manifest: true,\n emptyOutDir: true,\n outDir: '../out/client',\n rollupOptions: {\n input: CLIENT_MAIN_ENTRY,\n },\n },\n },\n ),\n ),\n build(\n mergeConfig<InlineConfig, InlineConfig>(\n createBaseViteConfigFromOssidoConfig(config),\n {\n plugins: VITE_SSR_PLUGINS,\n build: {\n ssr: true,\n minify: true,\n outDir: '../out/server',\n emptyOutDir: true,\n rollupOptions: {\n input: SERVER_MAIN_ENTRY,\n output: {\n entryFileNames: 'prod-server.js',\n // ESM (not IIFE) so the SSR entry can use top-level `await`;\n // the Rust runtime compiles it as a V8 module\n // (`Ssr::from_module`). The build stays a single\n // `prod-server.js` — the only file the runtime reads —\n // because the SSR build disables code splitting.\n format: 'es',\n },\n },\n },\n ssr: {\n target: 'webworker',\n noExternal: true,\n },\n },\n ),\n ),\n ]);\n\n printBundleSummary();\n });\n};\n\nconst buildConfig = (): void => {\n blockingAsync(async (): Promise<void> => {\n await build({\n root: '.ossido',\n // `'error'` (not `'silent'`) so config build failures are visible.\n logLevel: 'error',\n cacheDir: 'cache',\n envDir: '../',\n build: {\n ssr: true,\n outDir: 'config',\n emptyOutDir: true,\n rollupOptions: {\n input: './ossido.config.ts',\n output: {\n entryFileNames: 'config.mjs',\n },\n },\n },\n });\n\n const config = await loadConfig();\n await createJsonConfig(config);\n });\n};\n\n/**\n * Run a user-defined build lifecycle hook (`prebuild` / `postbuild`) from\n * `ossido.config.ts`. Invoked by the Rust CLI (`ossido-run-build-hook <name>`)\n * at the matching point of `ossido build`. No-ops when the hook is undefined.\n *\n * The output/public dirs and mode mirror what the build produces: static builds\n * deploy `out/static`, server builds `out`. `postbuild` gets the emitted-file\n * manifest; `prebuild` runs before anything is emitted, so its manifest is empty.\n */\nconst runBuildHook = (name: 'prebuild' | 'postbuild'): void =>\n blockingAsync(async () => {\n const config = await loadConfig();\n const hook = config.build?.[name];\n if (typeof hook !== 'function') return;\n\n const mode = process.env.OSSIDO_STATIC === 'true' ? 'static' : 'server';\n const outputDirectory = resolve(mode === 'static' ? 'out/static' : 'out');\n const publicDirectory = resolve('public');\n const manifest =\n name === 'postbuild' ? listFilesRecursive(outputDirectory) : [];\n\n // `loadConfig` returns the resolved `InternalOssidoConfig` (a structural\n // superset of the public `OssidoConfig` the hook context exposes).\n await hook({\n mode,\n outputDirectory,\n publicDirectory,\n manifest,\n config: config as OssidoConfig,\n });\n });\n\nexport {\n buildProd,\n buildConfig,\n developmentCSRWatch,\n developmentSSRBundle,\n runBuildHook,\n};\n"],"mappings":"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"}
|
|
@@ -19,11 +19,15 @@ export interface InternalOssidoConfigDev {
|
|
|
19
19
|
export interface InternalOssidoConfigSsr {
|
|
20
20
|
renderThreads: number | null;
|
|
21
21
|
}
|
|
22
|
-
export interface InternalOssidoConfig extends Omit<OssidoConfig, 'server' | 'logging' | 'dev' | 'ssr' | 'output' | 'viewTransitions'> {
|
|
22
|
+
export interface InternalOssidoConfig extends Omit<OssidoConfig, 'server' | 'logging' | 'dev' | 'ssr' | 'output' | 'viewTransitions' | 'env'> {
|
|
23
23
|
server: OssidoConfigServer;
|
|
24
24
|
logging: InternalOssidoConfigLogging;
|
|
25
25
|
dev: InternalOssidoConfigDev;
|
|
26
26
|
ssr: InternalOssidoConfigSsr;
|
|
27
27
|
output: OssidoConfigOutput;
|
|
28
28
|
viewTransitions: boolean;
|
|
29
|
+
/** Normalized to an array (a single configured path is wrapped). */
|
|
30
|
+
env?: Array<string>;
|
|
31
|
+
/** Build-only; consumed by the Vite config, not written to `config.json`. */
|
|
32
|
+
lightningcss?: OssidoConfig['lightningcss'];
|
|
29
33
|
}
|
|
@@ -3,17 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The HTTP method is the client method name (`apiClient.post(...)`) so the URL,
|
|
5
5
|
* route params, and the `.json()` response are all inferred from the method +
|
|
6
|
-
* path. The route map is generated into `.ossido/types.ts` as
|
|
7
|
-
*
|
|
6
|
+
* path. The route map is generated into `.ossido/types.ts` as a merge into the
|
|
7
|
+
* global `OssidoApiRoutes` interface below:
|
|
8
8
|
*
|
|
9
9
|
* ```ts
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* "/api/pokemons/[name]": {
|
|
14
|
-
* GET: { params: { name: string }; response: import("@ossido-labs/ossido/types").Pokemon }
|
|
15
|
-
* }
|
|
16
|
-
* }
|
|
10
|
+
* interface OssidoApiRoutes {
|
|
11
|
+
* "/api/pokemons/[name]": {
|
|
12
|
+
* GET: { params: { name: string }; response: import("@ossido-labs/ossido/types").Pokemon }
|
|
17
13
|
* }
|
|
18
14
|
* }
|
|
19
15
|
* ```
|
|
@@ -27,11 +23,20 @@
|
|
|
27
23
|
*/
|
|
28
24
|
type MethodName = 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
29
25
|
type HttpMethod = Uppercase<MethodName>;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
declare global {
|
|
27
|
+
/**
|
|
28
|
+
* The project's API route map (path → method → `{ params, response }`). The
|
|
29
|
+
* generated `.ossido/types.ts` merges the concrete routes into this global
|
|
30
|
+
* interface (a matching `interface OssidoApiRoutes { ... }`). Empty until
|
|
31
|
+
* generated.
|
|
32
|
+
*
|
|
33
|
+
* A **global** interface rather than a module-augmentation target on purpose:
|
|
34
|
+
* `.ossido/types.ts` is a global script, so a `declare module` there would
|
|
35
|
+
* *shadow* this module (hiding `apiClient`) instead of augmenting it — global
|
|
36
|
+
* interfaces merge across files without that hazard.
|
|
37
|
+
*/
|
|
38
|
+
interface OssidoApiRoutes {
|
|
39
|
+
}
|
|
35
40
|
}
|
|
36
41
|
interface ApiRouteEntry {
|
|
37
42
|
params: Record<string, string>;
|
|
@@ -41,10 +46,13 @@ type AnyApiRoutes = Record<string, Partial<Record<HttpMethod, ApiRouteEntry>>>;
|
|
|
41
46
|
/**
|
|
42
47
|
* The registered routes, or a permissive fallback when a project hasn't
|
|
43
48
|
* generated its types yet (so the client stays usable, just untyped).
|
|
49
|
+
*
|
|
50
|
+
* Used directly (no `extends AnyApiRoutes` guard): `OssidoApiRoutes` is an
|
|
51
|
+
* interface, which has no implicit index signature and so never satisfies the
|
|
52
|
+
* `Record<string, …>` shape — `PathsFor`/`EntryFor` operate on it structurally
|
|
53
|
+
* instead, and `EntryFor` carries its own per-entry fallback.
|
|
44
54
|
*/
|
|
45
|
-
type ApiRoutes =
|
|
46
|
-
apiRoutes: infer R;
|
|
47
|
-
} ? R extends AnyApiRoutes ? R : AnyApiRoutes : AnyApiRoutes;
|
|
55
|
+
type ApiRoutes = keyof OssidoApiRoutes extends never ? AnyApiRoutes : OssidoApiRoutes;
|
|
48
56
|
/** Paths that serve the given HTTP method. */
|
|
49
57
|
type PathsFor<M extends HttpMethod> = {
|
|
50
58
|
[P in keyof ApiRoutes]: M extends keyof ApiRoutes[P] ? P : never;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/client/index.ts"],"sourcesContent":["/**\n * A typed `fetch` client for a project's `#[api(METHOD)]` routes.\n *\n * The HTTP method is the client method name (`apiClient.post(...)`) so the URL,\n * route params, and the `.json()` response are all inferred from the method +\n * path. The route map is generated into `.ossido/types.ts` as
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/client/index.ts"],"sourcesContent":["/**\n * A typed `fetch` client for a project's `#[api(METHOD)]` routes.\n *\n * The HTTP method is the client method name (`apiClient.post(...)`) so the URL,\n * route params, and the `.json()` response are all inferred from the method +\n * path. The route map is generated into `.ossido/types.ts` as a merge into the\n * global `OssidoApiRoutes` interface below:\n *\n * ```ts\n * interface OssidoApiRoutes {\n * \"/api/pokemons/[name]\": {\n * GET: { params: { name: string }; response: import(\"@ossido-labs/ossido/types\").Pokemon }\n * }\n * }\n * ```\n *\n * ```ts\n * import { apiClient } from '@ossido-labs/ossido/client'\n *\n * const res = await apiClient.get('/api/pokemons/[name]', { params: { name: 'pikachu' } })\n * const pokemon = await res.json() //=> Pokemon\n * ```\n */\n\ntype MethodName = 'get' | 'post' | 'put' | 'patch' | 'delete';\ntype HttpMethod = Uppercase<MethodName>;\n\ndeclare global {\n /**\n * The project's API route map (path → method → `{ params, response }`). The\n * generated `.ossido/types.ts` merges the concrete routes into this global\n * interface (a matching `interface OssidoApiRoutes { ... }`). Empty until\n * generated.\n *\n * A **global** interface rather than a module-augmentation target on purpose:\n * `.ossido/types.ts` is a global script, so a `declare module` there would\n * *shadow* this module (hiding `apiClient`) instead of augmenting it — global\n * interfaces merge across files without that hazard.\n */\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface OssidoApiRoutes {}\n}\n\n// Route params are always URL-segment strings; an empty route has\n// `Record<string, never>` (no fillable segments).\ninterface ApiRouteEntry {\n params: Record<string, string>;\n response: unknown;\n}\ntype AnyApiRoutes = Record<string, Partial<Record<HttpMethod, ApiRouteEntry>>>;\n\n/**\n * The registered routes, or a permissive fallback when a project hasn't\n * generated its types yet (so the client stays usable, just untyped).\n *\n * Used directly (no `extends AnyApiRoutes` guard): `OssidoApiRoutes` is an\n * interface, which has no implicit index signature and so never satisfies the\n * `Record<string, …>` shape — `PathsFor`/`EntryFor` operate on it structurally\n * instead, and `EntryFor` carries its own per-entry fallback.\n */\ntype ApiRoutes = keyof OssidoApiRoutes extends never\n ? AnyApiRoutes\n : OssidoApiRoutes;\n\n/** Paths that serve the given HTTP method. */\ntype PathsFor<M extends HttpMethod> = {\n [P in keyof ApiRoutes]: M extends keyof ApiRoutes[P] ? P : never;\n}[keyof ApiRoutes] &\n string;\n\ntype EntryFor<\n P extends string,\n M extends HttpMethod,\n> = P extends keyof ApiRoutes\n ? M extends keyof ApiRoutes[P]\n ? ApiRoutes[P][M] extends ApiRouteEntry\n ? ApiRoutes[P][M]\n : ApiRouteEntry\n : ApiRouteEntry\n : ApiRouteEntry;\n\ntype ParamsOf<P extends string, M extends HttpMethod> = EntryFor<\n P,\n M\n>['params'];\ntype ResponseOf<P extends string, M extends HttpMethod> = EntryFor<\n P,\n M\n>['response'];\n\n/**\n * Whether a params object *requires* any key. `{} extends T` is true only when\n * every property of `T` is optional/absent — so a static route\n * (`Record<string, never>`) is `false` and a dynamic one (`{ id: string }`) is\n * `true`. (`keyof T extends never` would be wrong: `keyof Record<string, never>`\n * is `string`, not `never`.)\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\ntype HasKeys<T> = {} extends T ? false : true;\n\n/** A `fetch` {@link Response} whose `json()` is typed to the route's response. */\nexport interface TypedResponse<T> extends Response {\n json(): Promise<T>;\n}\n\n/**\n * `fetch` options minus `method` (the method is the client method name), plus\n * `params` to fill the URL's `[segments]` — required only for dynamic routes.\n */\nexport type RequestOptions<Params> = Omit<RequestInit, 'method'> &\n (HasKeys<Params> extends true\n ? { params: Params }\n : { params?: Record<string, never> });\n\ntype MethodFn<M extends HttpMethod> = <P extends PathsFor<M>>(\n path: P,\n ...args: HasKeys<ParamsOf<P, M>> extends true\n ? [options: RequestOptions<ParamsOf<P, M>>]\n : [options?: RequestOptions<ParamsOf<P, M>>]\n) => Promise<TypedResponse<ResponseOf<P, M>>>;\n\nexport type ApiClient = { [M in MethodName]: MethodFn<Uppercase<M>> };\n\nexport interface CreateApiClientOptions {\n /** Prepended to every request path (e.g. an absolute origin for SSR fetches). */\n baseUrl?: string;\n /** Default `fetch` options merged into every request. */\n defaults?: Omit<RequestInit, 'method'>;\n /** Custom `fetch` implementation (defaults to the global `fetch`). */\n fetch?: typeof fetch;\n}\n\n// Matches `[name]` and `[...slug]`, capturing the spread marker and the name.\nconst PARAM_SEGMENT = /\\[(\\.\\.\\.)?([^\\]]+)\\]/g;\n\n/** Substitute `params` into a path template's `[segments]`. */\nfunction buildPath(path: string, params?: Record<string, unknown>): string {\n if (!params) return path;\n return path.replace(\n PARAM_SEGMENT,\n (_match, spread: string | undefined, name: string) => {\n const value = params[name];\n if (value == null) {\n throw new TypeError(\n `Missing value for route param \"${name}\" in \"${path}\"`,\n );\n }\n const str = String(value);\n // A catch-all (`[...slug]`) spans multiple segments, so keep its slashes.\n return spread\n ? str.split('/').map(encodeURIComponent).join('/')\n : encodeURIComponent(str);\n },\n );\n}\n\n/** Create an {@link ApiClient}, optionally with a base URL and default options. */\nexport function createApiClient(\n options: CreateApiClientOptions = {},\n): ApiClient {\n const { baseUrl = '', defaults, fetch: fetchImpl } = options;\n\n const request =\n (method: HttpMethod) =>\n (\n path: string,\n opts: RequestInit & { params?: Record<string, unknown> } = {},\n ): Promise<Response> => {\n const { params, ...init } = opts;\n const url = baseUrl + buildPath(path, params);\n return (fetchImpl ?? fetch)(url, { ...defaults, ...init, method });\n };\n\n return {\n get: request('GET'),\n post: request('POST'),\n put: request('PUT'),\n patch: request('PATCH'),\n delete: request('DELETE'),\n } as ApiClient;\n}\n\n/** The default client — global `fetch`, relative paths. */\nexport const apiClient: ApiClient = createApiClient();\n"],"mappings":";AAqIA,MAAM,gBAAgB;;AAGtB,SAAS,UAAU,MAAc,QAA0C;CACzE,IAAI,CAAC,QAAQ,OAAO;CACpB,OAAO,KAAK,QACV,gBACC,QAAQ,QAA4B,SAAiB;EACpD,MAAM,QAAQ,OAAO;EACrB,IAAI,SAAS,MACX,MAAM,IAAI,UACR,kCAAkC,KAAK,QAAQ,KAAK,EACtD;EAEF,MAAM,MAAM,OAAO,KAAK;EAExB,OAAO,SACH,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG,IAC/C,mBAAmB,GAAG;CAC5B,CACF;AACF;;AAGA,SAAgB,gBACd,UAAkC,CAAC,GACxB;CACX,MAAM,EAAE,UAAU,IAAI,UAAU,OAAO,cAAc;CAErD,MAAM,WACH,YAEC,MACA,OAA2D,CAAC,MACtC;EACtB,MAAM,EAAE,QAAQ,GAAG,SAAS;EAC5B,MAAM,MAAM,UAAU,UAAU,MAAM,MAAM;EAC5C,QAAQ,aAAa,MAAK,CAAE,KAAK;GAAE,GAAG;GAAU,GAAG;GAAM;EAAO,CAAC;CACnE;CAEF,OAAO;EACL,KAAK,QAAQ,KAAK;EAClB,MAAM,QAAQ,MAAM;EACpB,KAAK,QAAQ,KAAK;EAClB,OAAO,QAAQ,OAAO;EACtB,QAAQ,QAAQ,QAAQ;CAC1B;AACF;;AAGA,MAAa,YAAuB,gBAAgB"}
|
|
@@ -54,6 +54,31 @@ export interface OssidoConfig {
|
|
|
54
54
|
output?: OssidoConfigOutput;
|
|
55
55
|
/** Build lifecycle hooks (`ossido build`, both output modes; not `dev`). */
|
|
56
56
|
build?: OssidoConfigBuild;
|
|
57
|
+
/**
|
|
58
|
+
* Enable [Lightning CSS](https://lightningcss.dev/) — a fast, Rust-based CSS
|
|
59
|
+
* transformer + minifier — in place of Vite's default PostCSS pipeline. Off by
|
|
60
|
+
* default.
|
|
61
|
+
*
|
|
62
|
+
* `true` enables it with default browser targets; pass an object to override
|
|
63
|
+
* `targets`.
|
|
64
|
+
*
|
|
65
|
+
* NOTE: enabling this replaces PostCSS entirely (a `postcss.config.js` is
|
|
66
|
+
* ignored) and changes CSS-modules class hashing. Vite plugins such as
|
|
67
|
+
* `@tailwindcss/vite` are unaffected — they run as plugins, not through the
|
|
68
|
+
* CSS transformer.
|
|
69
|
+
*/
|
|
70
|
+
lightningcss?: boolean | OssidoLightningCss;
|
|
71
|
+
/**
|
|
72
|
+
* Override which `.env` file(s) are loaded, as a path or array of paths
|
|
73
|
+
* (relative to the project root, loaded in order — a later file overrides an
|
|
74
|
+
* earlier one). When set, this **replaces** the default
|
|
75
|
+
* `.env` / `.env.local` / `.env.[mode]` cascade. Leave unset to keep the
|
|
76
|
+
* default cascade.
|
|
77
|
+
*
|
|
78
|
+
* Only meaningful alongside an `#[ossido::Environment]` struct, which defines
|
|
79
|
+
* the typed schema these variables populate.
|
|
80
|
+
*/
|
|
81
|
+
env?: string | Array<string>;
|
|
57
82
|
/**
|
|
58
83
|
* Animate client-side navigations with the browser
|
|
59
84
|
* [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API).
|
|
@@ -68,6 +93,16 @@ export interface OssidoConfig {
|
|
|
68
93
|
}
|
|
69
94
|
/** Build output mode. */
|
|
70
95
|
export type OssidoConfigOutput = 'static' | 'server';
|
|
96
|
+
/** Lightning CSS options. `true` enables it with default browser targets. */
|
|
97
|
+
export interface OssidoLightningCss {
|
|
98
|
+
/**
|
|
99
|
+
* [browserslist](https://github.com/browserslist/browserslist) query used to
|
|
100
|
+
* derive Lightning CSS targets (autoprefixing + modern-CSS lowering).
|
|
101
|
+
* Defaults to `'defaults'`. The query is used verbatim — the project's
|
|
102
|
+
* `.browserslistrc` / `package.json` `browserslist` field is not consulted.
|
|
103
|
+
*/
|
|
104
|
+
targets?: string;
|
|
105
|
+
}
|
|
71
106
|
/**
|
|
72
107
|
* Everything a build hook is given about the current `ossido build`.
|
|
73
108
|
*/
|
package/dist/esm/constants.d.ts
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
1
|
export declare const SERVER_PAYLOAD_VARIABLE_NAME = "__OSSIDO_SERVER_PAYLOAD__";
|
|
2
|
+
/**
|
|
3
|
+
* Global holding the project's `#[public]` environment variables. Set on the
|
|
4
|
+
* browser by a `<script>` from `OssidoScripts`, and on the server (V8) by the
|
|
5
|
+
* SSR renderer before rendering. Read by `getEnv` (`@ossido-labs/ossido/env`).
|
|
6
|
+
*/
|
|
7
|
+
export declare const PUBLIC_ENV_VARIABLE_NAME = "__OSSIDO_PUBLIC_ENV__";
|
package/dist/esm/constants.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
//#region src/constants.ts
|
|
2
2
|
const SERVER_PAYLOAD_VARIABLE_NAME = "__OSSIDO_SERVER_PAYLOAD__";
|
|
3
|
+
/**
|
|
4
|
+
* Global holding the project's `#[public]` environment variables. Set on the
|
|
5
|
+
* browser by a `<script>` from `OssidoScripts`, and on the server (V8) by the
|
|
6
|
+
* SSR renderer before rendering. Read by `getEnv` (`@ossido-labs/ossido/env`).
|
|
7
|
+
*/
|
|
8
|
+
const PUBLIC_ENV_VARIABLE_NAME = "__OSSIDO_PUBLIC_ENV__";
|
|
3
9
|
|
|
4
10
|
//#endregion
|
|
5
|
-
export { SERVER_PAYLOAD_VARIABLE_NAME };
|
|
11
|
+
export { PUBLIC_ENV_VARIABLE_NAME, SERVER_PAYLOAD_VARIABLE_NAME };
|
|
6
12
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":[],"sources":["../../src/constants.ts"],"sourcesContent":["export const SERVER_PAYLOAD_VARIABLE_NAME = '__OSSIDO_SERVER_PAYLOAD__';\n"],"mappings":";AAAA,MAAa,+BAA+B"}
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../src/constants.ts"],"sourcesContent":["export const SERVER_PAYLOAD_VARIABLE_NAME = '__OSSIDO_SERVER_PAYLOAD__';\n\n/**\n * Global holding the project's `#[public]` environment variables. Set on the\n * browser by a `<script>` from `OssidoScripts`, and on the server (V8) by the\n * SSR renderer before rendering. Read by `getEnv` (`@ossido-labs/ossido/env`).\n */\nexport const PUBLIC_ENV_VARIABLE_NAME = '__OSSIDO_PUBLIC_ENV__';\n"],"mappings":";AAAA,MAAa,+BAA+B;;;;;;AAO5C,MAAa,2BAA2B"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed access to a project's **public** environment variables — the `#[public]`
|
|
3
|
+
* fields of its `#[ossido::Environment]` Rust struct.
|
|
4
|
+
*
|
|
5
|
+
* The Rust build serializes the public fields into the `__OSSIDO_PUBLIC_ENV__`
|
|
6
|
+
* global (injected into the SSR HTML for the browser, and set on `globalThis`
|
|
7
|
+
* during server rendering). The variable map is generated into `.ossido/types.ts`
|
|
8
|
+
* as a merge into the global `OssidoPublicEnv` interface below:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* interface OssidoPublicEnv {
|
|
12
|
+
* api_url: string
|
|
13
|
+
* port: number
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { getEnv } from '@ossido-labs/ossido/env'
|
|
19
|
+
*
|
|
20
|
+
* const apiUrl = getEnv('api_url') //=> string
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Private (non-`#[public]`) fields never leave the server and are not accessible
|
|
24
|
+
* here — read those in Rust with `ossido::get_env!`.
|
|
25
|
+
*/
|
|
26
|
+
declare global {
|
|
27
|
+
/**
|
|
28
|
+
* Describes the project's public environment variables. The generated
|
|
29
|
+
* `.ossido/types.ts` merges the concrete keys into this global interface (a
|
|
30
|
+
* matching `interface OssidoPublicEnv { ... }`). Empty until generated.
|
|
31
|
+
*
|
|
32
|
+
* This is a **global** interface rather than a module-augmentation target on
|
|
33
|
+
* purpose: `.ossido/types.ts` is a global script, so a `declare module` there
|
|
34
|
+
* would *shadow* this module (hiding `getEnv`) instead of augmenting it —
|
|
35
|
+
* global interfaces merge across files without that hazard.
|
|
36
|
+
*/
|
|
37
|
+
interface OssidoPublicEnv {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The public env keys, or a permissive fallback when a project hasn't generated
|
|
42
|
+
* its types yet (so `getEnv` stays usable, just untyped).
|
|
43
|
+
*/
|
|
44
|
+
export type PublicEnv = keyof OssidoPublicEnv extends never ? Record<string, unknown> : OssidoPublicEnv;
|
|
45
|
+
/**
|
|
46
|
+
* Read a public environment variable by key, returning a typed copy of its
|
|
47
|
+
* value.
|
|
48
|
+
*
|
|
49
|
+
* Throws if no public environment is available — i.e. the project defines no
|
|
50
|
+
* `#[ossido::Environment]` struct (the Rust equivalent, `get_env!`, is a compile
|
|
51
|
+
* error in that case) — or if the key is not a known public variable.
|
|
52
|
+
*/
|
|
53
|
+
export declare function getEnv<K extends keyof PublicEnv>(key: K): PublicEnv[K];
|
|
54
|
+
/**
|
|
55
|
+
* Read a public environment variable, collapsing an optional value to a concrete
|
|
56
|
+
* `T`: `fallback` is returned whenever the value is absent (an optional variable
|
|
57
|
+
* unset, or no public environment available). The `null`/`undefined` member is
|
|
58
|
+
* stripped from the return type.
|
|
59
|
+
*
|
|
60
|
+
* ```ts
|
|
61
|
+
* // env type: { analytics_enabled: boolean | null }
|
|
62
|
+
* const analytics = getEnv('analytics_enabled', false); //=> boolean
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function getEnv<K extends keyof PublicEnv>(key: K, fallback: NonNullable<PublicEnv[K]>): NonNullable<PublicEnv[K]>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PUBLIC_ENV_VARIABLE_NAME } from "../constants.js";
|
|
2
|
+
|
|
3
|
+
//#region src/env/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* Typed access to a project's **public** environment variables — the `#[public]`
|
|
6
|
+
* fields of its `#[ossido::Environment]` Rust struct.
|
|
7
|
+
*
|
|
8
|
+
* The Rust build serializes the public fields into the `__OSSIDO_PUBLIC_ENV__`
|
|
9
|
+
* global (injected into the SSR HTML for the browser, and set on `globalThis`
|
|
10
|
+
* during server rendering). The variable map is generated into `.ossido/types.ts`
|
|
11
|
+
* as a merge into the global `OssidoPublicEnv` interface below:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* interface OssidoPublicEnv {
|
|
15
|
+
* api_url: string
|
|
16
|
+
* port: number
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { getEnv } from '@ossido-labs/ossido/env'
|
|
22
|
+
*
|
|
23
|
+
* const apiUrl = getEnv('api_url') //=> string
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Private (non-`#[public]`) fields never leave the server and are not accessible
|
|
27
|
+
* here — read those in Rust with `ossido::get_env!`.
|
|
28
|
+
*/
|
|
29
|
+
function getEnv(key, fallback) {
|
|
30
|
+
const hasFallback = fallback !== void 0;
|
|
31
|
+
const env = globalThis[PUBLIC_ENV_VARIABLE_NAME];
|
|
32
|
+
if (env == null) {
|
|
33
|
+
if (hasFallback) return fallback;
|
|
34
|
+
throw new Error("getEnv: no public environment is available. Define an `Environment` struct with `#[ossido::Environment]` and mark fields `#[public]` to expose them.");
|
|
35
|
+
}
|
|
36
|
+
if (!(key in env)) {
|
|
37
|
+
if (hasFallback) return fallback;
|
|
38
|
+
throw new Error(`getEnv: "${String(key)}" is not a public environment variable.`);
|
|
39
|
+
}
|
|
40
|
+
const value = env[key];
|
|
41
|
+
if (hasFallback && (value === null || value === void 0)) return fallback;
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { getEnv };
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/env/index.ts"],"sourcesContent":["/**\n * Typed access to a project's **public** environment variables — the `#[public]`\n * fields of its `#[ossido::Environment]` Rust struct.\n *\n * The Rust build serializes the public fields into the `__OSSIDO_PUBLIC_ENV__`\n * global (injected into the SSR HTML for the browser, and set on `globalThis`\n * during server rendering). The variable map is generated into `.ossido/types.ts`\n * as a merge into the global `OssidoPublicEnv` interface below:\n *\n * ```ts\n * interface OssidoPublicEnv {\n * api_url: string\n * port: number\n * }\n * ```\n *\n * ```ts\n * import { getEnv } from '@ossido-labs/ossido/env'\n *\n * const apiUrl = getEnv('api_url') //=> string\n * ```\n *\n * Private (non-`#[public]`) fields never leave the server and are not accessible\n * here — read those in Rust with `ossido::get_env!`.\n */\n\nimport { PUBLIC_ENV_VARIABLE_NAME } from '../constants';\n\ndeclare global {\n /**\n * Describes the project's public environment variables. The generated\n * `.ossido/types.ts` merges the concrete keys into this global interface (a\n * matching `interface OssidoPublicEnv { ... }`). Empty until generated.\n *\n * This is a **global** interface rather than a module-augmentation target on\n * purpose: `.ossido/types.ts` is a global script, so a `declare module` there\n * would *shadow* this module (hiding `getEnv`) instead of augmenting it —\n * global interfaces merge across files without that hazard.\n */\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n interface OssidoPublicEnv {}\n}\n\n/**\n * The public env keys, or a permissive fallback when a project hasn't generated\n * its types yet (so `getEnv` stays usable, just untyped).\n */\nexport type PublicEnv = keyof OssidoPublicEnv extends never\n ? Record<string, unknown>\n : OssidoPublicEnv;\n\n/**\n * Read a public environment variable by key, returning a typed copy of its\n * value.\n *\n * Throws if no public environment is available — i.e. the project defines no\n * `#[ossido::Environment]` struct (the Rust equivalent, `get_env!`, is a compile\n * error in that case) — or if the key is not a known public variable.\n */\nexport function getEnv<K extends keyof PublicEnv>(key: K): PublicEnv[K];\n/**\n * Read a public environment variable, collapsing an optional value to a concrete\n * `T`: `fallback` is returned whenever the value is absent (an optional variable\n * unset, or no public environment available). The `null`/`undefined` member is\n * stripped from the return type.\n *\n * ```ts\n * // env type: { analytics_enabled: boolean | null }\n * const analytics = getEnv('analytics_enabled', false); //=> boolean\n * ```\n */\nexport function getEnv<K extends keyof PublicEnv>(\n key: K,\n fallback: NonNullable<PublicEnv[K]>,\n): NonNullable<PublicEnv[K]>;\nexport function getEnv<K extends keyof PublicEnv>(\n key: K,\n fallback?: NonNullable<PublicEnv[K]>,\n): PublicEnv[K] {\n const hasFallback = fallback !== undefined;\n\n const env = (globalThis as Record<string, unknown>)[\n PUBLIC_ENV_VARIABLE_NAME\n ] as PublicEnv | undefined;\n\n if (env == null) {\n if (hasFallback) return fallback;\n throw new Error(\n 'getEnv: no public environment is available. Define an `Environment` struct ' +\n 'with `#[ossido::Environment]` and mark fields `#[public]` to expose them.',\n );\n }\n\n if (!(key in (env as object))) {\n if (hasFallback) return fallback;\n throw new Error(\n `getEnv: \"${String(key)}\" is not a public environment variable.`,\n );\n }\n\n const value = env[key];\n // An unset optional variable serializes to `null`; fall back to the concrete\n // default when one was provided.\n if (hasFallback && (value === null || value === undefined)) {\n return fallback;\n }\n return value;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,SAAgB,OACd,KACA,UACc;CACd,MAAM,cAAc,aAAa;CAEjC,MAAM,MAAO,WACX;CAGF,IAAI,OAAO,MAAM;EACf,IAAI,aAAa,OAAO;EACxB,MAAM,IAAI,MACR,sJAEF;CACF;CAEA,IAAI,EAAE,OAAQ,MAAiB;EAC7B,IAAI,aAAa,OAAO;EACxB,MAAM,IAAI,MACR,YAAY,OAAO,GAAG,EAAE,wCAC1B;CACF;CAEA,MAAM,QAAQ,IAAI;CAGlB,IAAI,gBAAgB,UAAU,QAAQ,UAAU,SAC9C,OAAO;CAET,OAAO;AACT"}
|
|
@@ -8,8 +8,8 @@ const DEFAULT_SERVER_CONFIG = {
|
|
|
8
8
|
};
|
|
9
9
|
const VITE_PROXY_PATH = "/vite-server";
|
|
10
10
|
const DevResources = ({ devServerConfig }) => {
|
|
11
|
-
const {
|
|
12
|
-
const viteBaseUrl = origin != null ? `${origin}${VITE_PROXY_PATH}` :
|
|
11
|
+
const { origin } = devServerConfig ?? DEFAULT_SERVER_CONFIG;
|
|
12
|
+
const viteBaseUrl = origin != null ? `${origin}${VITE_PROXY_PATH}` : VITE_PROXY_PATH;
|
|
13
13
|
/**
|
|
14
14
|
* These scripts must execute in order: the react-refresh preamble has to run
|
|
15
15
|
* (and set `__vite_plugin_react_preamble_installed__`) before `client-main`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DevResources.js","names":[],"sources":["../../../src/shared/DevResources.tsx"],"sourcesContent":["import type { JSX } from 'react';\n\nimport type { OssidoConfigServer } from '../config';\n\nconst DEFAULT_SERVER_CONFIG = { host: 'localhost', origin: null, port: 3000 };\nconst VITE_PROXY_PATH = '/vite-server';\n\ninterface DevResourcesProps {\n devServerConfig?: OssidoConfigServer;\n}\n\nexport const DevResources = ({\n devServerConfig,\n}: DevResourcesProps): JSX.Element => {\n const {
|
|
1
|
+
{"version":3,"file":"DevResources.js","names":[],"sources":["../../../src/shared/DevResources.tsx"],"sourcesContent":["import type { JSX } from 'react';\n\nimport type { OssidoConfigServer } from '../config';\n\nconst DEFAULT_SERVER_CONFIG = { host: 'localhost', origin: null, port: 3000 };\nconst VITE_PROXY_PATH = '/vite-server';\n\ninterface DevResourcesProps {\n devServerConfig?: OssidoConfigServer;\n}\n\nexport const DevResources = ({\n devServerConfig,\n}: DevResourcesProps): JSX.Element => {\n const { origin } = devServerConfig ?? DEFAULT_SERVER_CONFIG;\n\n // The vite assets are proxied through *this* ossido server (same origin), so a\n // relative URL is correct and resolves against whatever address the browser\n // used — which is what makes `host: '0.0.0.0'` and LAN access work (an\n // absolute `http://0.0.0.0:port/...` is unreachable from a browser). An\n // explicit `origin` (e.g. a custom dev domain behind a proxy) still wins.\n const viteBaseUrl =\n origin != null ? `${origin}${VITE_PROXY_PATH}` : VITE_PROXY_PATH;\n\n /**\n * These scripts must execute in order: the react-refresh preamble has to run\n * (and set `__vite_plugin_react_preamble_installed__`) before `client-main`\n * loads any React component, otherwise `@vitejs/plugin-react-swc` throws\n * \"can't detect preamble\". `type=\"module\"` already defers execution without\n * blocking parsing, so `async` only removes the ordering guarantee and must\n * NOT be used here — it caused an intermittent preamble race.\n */\n return (\n <>\n <script type=\"module\">\n {[\n `import RefreshRuntime from '${viteBaseUrl}/@react-refresh'`,\n 'RefreshRuntime.injectIntoGlobalHook(window)',\n 'window.$RefreshReg$ = () => {}',\n 'window.$RefreshSig$ = () => (type) => type',\n 'window.__vite_plugin_react_preamble_installed__ = true',\n ].join('\\n')}\n </script>\n <script type=\"module\" src={`${viteBaseUrl}/@vite/client`}></script>\n <script type=\"module\" src={`${viteBaseUrl}/client-main.tsx`}></script>\n </>\n );\n};\n"],"mappings":";;;AAIA,MAAM,wBAAwB;CAAE,MAAM;CAAa,QAAQ;CAAM,MAAM;AAAK;AAC5E,MAAM,kBAAkB;AAMxB,MAAa,gBAAgB,EAC3B,sBACoC;CACpC,MAAM,EAAE,WAAW,mBAAmB;CAOtC,MAAM,cACJ,UAAU,OAAO,GAAG,SAAS,oBAAoB;;;;;;;;;CAUnD,OACE;EACE,oBAAC,UAAD;GAAQ,MAAK;aACV;IACC,+BAA+B,YAAY;IAC3C;IACA;IACA;IACA;GACF,CAAC,CAAC,KAAK,IAAI;EACL;EACR,oBAAC,UAAD;GAAQ,MAAK;GAAS,KAAK,GAAG,YAAY;EAAwB;EAClE,oBAAC,UAAD;GAAQ,MAAK;GAAS,KAAK,GAAG,YAAY;EAA2B;CACrE;AAEN"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SERVER_PAYLOAD_VARIABLE_NAME } from "../constants.js";
|
|
1
|
+
import { PUBLIC_ENV_VARIABLE_NAME, SERVER_PAYLOAD_VARIABLE_NAME } from "../constants.js";
|
|
2
2
|
import { DevResources } from "./DevResources.js";
|
|
3
3
|
import { ProdResources } from "./ProdResources.js";
|
|
4
4
|
import { useOssidoContextRawServerPayload, useOssidoContextServerPayload } from "./ossido-context.js";
|
|
@@ -21,6 +21,10 @@ function OssidoScripts() {
|
|
|
21
21
|
suppressHydrationWarning: true,
|
|
22
22
|
dangerouslySetInnerHTML: { __html: `window['${SERVER_PAYLOAD_VARIABLE_NAME}']=${escapeForScript(payloadJson)}` }
|
|
23
23
|
}),
|
|
24
|
+
serverPayload.publicEnv !== void 0 && /* @__PURE__ */ jsx("script", {
|
|
25
|
+
suppressHydrationWarning: true,
|
|
26
|
+
dangerouslySetInnerHTML: { __html: `window['${"__OSSIDO_PUBLIC_ENV__"}']=${escapeForScript(JSON.stringify(serverPayload.publicEnv))}` }
|
|
27
|
+
}),
|
|
24
28
|
serverPayload.mode === "Dev" && /* @__PURE__ */ jsx(DevResources, { devServerConfig: serverPayload.devServerConfig }),
|
|
25
29
|
serverPayload.mode === "Prod" && /* @__PURE__ */ jsx(ProdResources, {
|
|
26
30
|
jsBundles: serverPayload.jsBundles,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OssidoScripts.js","names":[],"sources":["../../../src/shared/OssidoScripts.tsx"],"sourcesContent":["import type { JSX } from 'react';\n\nimport {
|
|
1
|
+
{"version":3,"file":"OssidoScripts.js","names":[],"sources":["../../../src/shared/OssidoScripts.tsx"],"sourcesContent":["import type { JSX } from 'react';\n\nimport {\n SERVER_PAYLOAD_VARIABLE_NAME,\n PUBLIC_ENV_VARIABLE_NAME,\n} from '../constants';\n\nimport { DevResources } from './DevResources';\nimport { ProdResources } from './ProdResources';\nimport {\n useOssidoContextRawServerPayload,\n useOssidoContextServerPayload,\n} from './ossido-context';\n\n/**\n * The payload is embedded as a JS expression inside a `<script>`, so a string\n * value containing `</script>` (or `<!--`) could otherwise break out of the tag.\n * Escaping `<` to its unicode form neutralises that while remaining valid JSON.\n */\nfunction escapeForScript(json: string): string {\n return json.replace(/</g, '\\\\u003c');\n}\n\nexport function OssidoScripts(): JSX.Element {\n const serverPayload = useOssidoContextServerPayload();\n const rawServerPayload = useOssidoContextRawServerPayload();\n\n // On the server, reuse the exact JSON the Rust runtime already produced —\n // avoiding a second full `JSON.stringify` of the payload inside V8. On the\n // client the raw string isn't available, so fall back to stringifying the\n // parsed payload; that output is discarded during hydration\n // (`suppressHydrationWarning` keeps the server-rendered script, which has\n // already executed and set `window[...]`).\n const payloadJson = rawServerPayload ?? JSON.stringify(serverPayload);\n\n return (\n <>\n <script\n suppressHydrationWarning\n dangerouslySetInnerHTML={{\n __html: `window['${SERVER_PAYLOAD_VARIABLE_NAME}']=${escapeForScript(payloadJson)}`,\n }}\n />\n {serverPayload.publicEnv !== undefined && (\n <script\n suppressHydrationWarning\n dangerouslySetInnerHTML={{\n __html: `window['${PUBLIC_ENV_VARIABLE_NAME}']=${escapeForScript(\n JSON.stringify(serverPayload.publicEnv),\n )}`,\n }}\n />\n )}\n {serverPayload.mode === 'Dev' && (\n <DevResources devServerConfig={serverPayload.devServerConfig} />\n )}\n {serverPayload.mode === 'Prod' && (\n <ProdResources\n jsBundles={serverPayload.jsBundles}\n cssBundles={serverPayload.cssBundles}\n />\n )}\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAmBA,SAAS,gBAAgB,MAAsB;CAC7C,OAAO,KAAK,QAAQ,MAAM,SAAS;AACrC;AAEA,SAAgB,gBAA6B;CAC3C,MAAM,gBAAgB,8BAA8B;CASpD,MAAM,cARmB,iCAQU,KAAK,KAAK,UAAU,aAAa;CAEpE,OACE;EACE,oBAAC,UAAD;GACE;GACA,yBAAyB,EACvB,QAAQ,WAAW,6BAA6B,KAAK,gBAAgB,WAAW,IAClF;EACD;EACA,cAAc,cAAc,UAC3B,oBAAC,UAAD;GACE;GACA,yBAAyB,EACvB,QAAQ,mCAAoC,KAAK,gBAC/C,KAAK,UAAU,cAAc,SAAS,CACxC,IACF;EACD;EAEF,cAAc,SAAS,SACtB,oBAAC,cAAD,EAAc,iBAAiB,cAAc,gBAAkB;EAEhE,cAAc,SAAS,UACtB,oBAAC,eAAD;GACE,WAAW,cAAc;GACzB,YAAY,cAAc;EAC3B;CAEH;AAEN"}
|
package/dist/esm/ssr/server.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PUBLIC_ENV_VARIABLE_NAME } from "../constants.js";
|
|
1
2
|
import "./polyfills/globalScope.js";
|
|
2
3
|
import { MessageChannelPolyfill } from "./polyfills/MessageChannel.js";
|
|
3
4
|
import { OssidoEntryPoint } from "../shared/OssidoEntryPoint.js";
|
|
@@ -16,6 +17,7 @@ import { prerender } from "react-dom/static";
|
|
|
16
17
|
function serverSideRendering(routeTree) {
|
|
17
18
|
const element = async (payload) => {
|
|
18
19
|
const serverPayload = payload ? JSON.parse(payload) : {};
|
|
20
|
+
if (serverPayload.publicEnv !== void 0) globalThis[PUBLIC_ENV_VARIABLE_NAME] = serverPayload.publicEnv;
|
|
19
21
|
const router = createRouter({ routeTree });
|
|
20
22
|
await preloadRouteChain(router, serverPayload.location?.pathname);
|
|
21
23
|
return /* @__PURE__ */ jsx(OssidoEntryPoint, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":[],"sources":["../../../src/ssr/server.tsx"],"sourcesContent":["// #region POLYFILLS\n/**\n * Ossido internally uses a V8 JS engine that implements very few\n * browser/node/deno APIs in order to make it super fast and\n * share it within a multi thread runtime.\n *\n * While this is the reason of its speed, server side rendering\n * requires some JS APIs that need to be polyfilled.\n *\n * We basically have three ways to polyfill APIs:\n * 1. Create them with rust and expose them directly through the V8 engine to\n * the JS source.\n * 2. Polyfill them at the beginning of the JS source\n * (what we are doing here)\n * 3. Inject them via rollup-inject plugin, when needed\n *\n * Q: Why not all the libraries can be just injected with rollup-inject?\n * A: Leaving to rollup the duty of linking them can cause to declare them after their usage.\n * The following APIs are JS classes, and are not hoisted, hence this might\n * cause ReferenceError(s).\n *\n * The best solution is to create these polyfills within the rust environment\n * and share the classes in the JS scope by passing them through the V8 engine\n * (best for speed and code quality).\n *\n * This function might be a good entry point for adding such polyfills\n * (see `Ssr::add_global_fn` in crates/ossido_ssr/src/ssr.rs)\n */\n// Must run before the polyfills below: aliases `global` to `globalThis` so\n// their UMD init IIFEs find a valid scope in the `window`/`global`-less\n// ossido_ssr V8 runtime instead of dereferencing `undefined`.\nimport './polyfills/globalScope';\nimport 'fast-text-encoding';\nimport 'url-search-params-polyfill';\n\n/* eslint-disable import/order, import/newline-after-import */\nimport { MessageChannelPolyfill } from './polyfills/MessageChannel';\n(function (\n scope: Partial<Pick<typeof globalThis, 'MessageChannel'>> = {},\n): void {\n scope['MessageChannel'] = scope['MessageChannel'] ?? MessageChannelPolyfill;\n})(this);\n/* eslint-enable import/order, import/newline-after-import */\n// #endregion POLYFILLS\n\nimport type { ReadableStream } from 'node:stream/web';\n\nimport type { JSX } from 'react';\nimport { renderToReadableStream } from 'react-dom/server';\nimport { prerender } from 'react-dom/static';\nimport { createRouter, preloadRouteChain } from '@ossido-labs/ossido-router';\nimport type { createRoute } from '@ossido-labs/ossido-router';\n\nimport { OssidoEntryPoint } from '../shared/OssidoEntryPoint';\nimport type { ServerPayload } from '../types';\n\nimport { streamToString, createUtf8Streamer } from './utils';\n\ntype RouteTree = ReturnType<typeof createRoute>;\n\n/**\n * Chunk sink injected by the Rust `ossido_ssr` runtime for streaming renders (see\n * `Ssr::render_to_stream`). Each call hands one HTML fragment to Rust, which\n * forwards it to the client immediately instead of buffering the whole page.\n */\ndeclare const __ssr_write: ((chunk: string) => void) | undefined;\n\ninterface ServerSideRenderer {\n /** Buffered render — resolves the whole page to a single HTML string. */\n renderFn: (payload: string | undefined) => Promise<string>;\n /** Streaming render — flushes each HTML chunk via `__ssr_write`. */\n renderStream: (payload: string | undefined) => Promise<void>;\n}\n\nexport function serverSideRendering(routeTree: RouteTree): ServerSideRenderer {\n // Build the React element for a request. Shared by the buffered and streaming\n // entry points so they render exactly the same tree.\n //\n // Async because the matched route's code (page + wrapping layouts) is\n // preloaded first: routes are `React.lazy`-wrapped, and rendering one that\n // hasn't loaded suspends its boundary, which pushes the page content into an\n // out-of-order late chunk (empty shell paints first — a visible flash on\n // every cold load). In the bundled SSR output the dynamic imports are\n // inlined, so this resolves in a microtask.\n const element = async (payload: string | undefined): Promise<JSX.Element> => {\n const serverPayload = (payload ? JSON.parse(payload) : {}) as ServerPayload;\n const router = createRouter({ routeTree });\n await preloadRouteChain(router, serverPayload.location?.pathname);\n return (\n // `rawServerPayload` is the exact JSON Rust already produced; passing it\n // lets `OssidoScripts` embed it verbatim instead of re-stringifying the\n // parsed payload inside V8.\n <OssidoEntryPoint\n router={router}\n serverPayload={serverPayload}\n rawServerPayload={payload}\n />\n );\n };\n\n return {\n /**\n * Buffered render: resolve the whole page to a single string. Used for\n * error pages, static export (SSG), `catch_all`, and the dev fallback —\n * anywhere the caller needs the complete HTML up front.\n */\n async renderFn(payload: string | undefined): Promise<string> {\n // `prerender` (react-dom/static) is the SSG-correct API: its promise\n // resolves from React's `onAllReady`, so `prelude` is the fully-settled\n // tree (every Suspense boundary resolved) — no `allReady` dance and no\n // risk of capturing a fallback. Requires the SSR event loop (macrotasks)\n // to settle, same as `renderToReadableStream`.\n const { prelude } = await prerender(await element(payload));\n return await streamToString(\n prelude as unknown as ReadableStream<Uint8Array>,\n );\n },\n\n /**\n * Streaming render: flush each HTML chunk to Rust via\n * `__ssr_write` as React produces it, so the shell reaches the\n * client without waiting for the full page. `renderToReadableStream`\n * rejects on a *shell* error before any chunk is written, so Rust can still\n * send a 500 instead of a partial 200 in that case.\n */\n async renderStream(payload: string | undefined): Promise<void> {\n const write = __ssr_write;\n if (typeof write !== 'function') {\n throw new Error('__ssr_write is not registered by the runtime');\n }\n\n const stream = await renderToReadableStream(await element(payload));\n\n const streamer = createUtf8Streamer();\n for await (const chunk of stream as unknown as ReadableStream<Uint8Array>) {\n const text = streamer.push(chunk);\n if (text) write(text);\n }\n const tail = streamer.flush();\n if (tail) write(tail);\n },\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../../../src/ssr/server.tsx"],"sourcesContent":["// #region POLYFILLS\n/**\n * Ossido internally uses a V8 JS engine that implements very few\n * browser/node/deno APIs in order to make it super fast and\n * share it within a multi thread runtime.\n *\n * While this is the reason of its speed, server side rendering\n * requires some JS APIs that need to be polyfilled.\n *\n * We basically have three ways to polyfill APIs:\n * 1. Create them with rust and expose them directly through the V8 engine to\n * the JS source.\n * 2. Polyfill them at the beginning of the JS source\n * (what we are doing here)\n * 3. Inject them via rollup-inject plugin, when needed\n *\n * Q: Why not all the libraries can be just injected with rollup-inject?\n * A: Leaving to rollup the duty of linking them can cause to declare them after their usage.\n * The following APIs are JS classes, and are not hoisted, hence this might\n * cause ReferenceError(s).\n *\n * The best solution is to create these polyfills within the rust environment\n * and share the classes in the JS scope by passing them through the V8 engine\n * (best for speed and code quality).\n *\n * This function might be a good entry point for adding such polyfills\n * (see `Ssr::add_global_fn` in crates/ossido_ssr/src/ssr.rs)\n */\n// Must run before the polyfills below: aliases `global` to `globalThis` so\n// their UMD init IIFEs find a valid scope in the `window`/`global`-less\n// ossido_ssr V8 runtime instead of dereferencing `undefined`.\nimport './polyfills/globalScope';\nimport 'fast-text-encoding';\nimport 'url-search-params-polyfill';\n\n/* eslint-disable import/order, import/newline-after-import */\nimport { MessageChannelPolyfill } from './polyfills/MessageChannel';\n(function (\n scope: Partial<Pick<typeof globalThis, 'MessageChannel'>> = {},\n): void {\n scope['MessageChannel'] = scope['MessageChannel'] ?? MessageChannelPolyfill;\n})(this);\n/* eslint-enable import/order, import/newline-after-import */\n// #endregion POLYFILLS\n\nimport type { ReadableStream } from 'node:stream/web';\n\nimport type { JSX } from 'react';\nimport { renderToReadableStream } from 'react-dom/server';\nimport { prerender } from 'react-dom/static';\nimport { createRouter, preloadRouteChain } from '@ossido-labs/ossido-router';\nimport type { createRoute } from '@ossido-labs/ossido-router';\n\nimport { OssidoEntryPoint } from '../shared/OssidoEntryPoint';\nimport { PUBLIC_ENV_VARIABLE_NAME } from '../constants';\nimport type { ServerPayload } from '../types';\n\nimport { streamToString, createUtf8Streamer } from './utils';\n\ntype RouteTree = ReturnType<typeof createRoute>;\n\n/**\n * Chunk sink injected by the Rust `ossido_ssr` runtime for streaming renders (see\n * `Ssr::render_to_stream`). Each call hands one HTML fragment to Rust, which\n * forwards it to the client immediately instead of buffering the whole page.\n */\ndeclare const __ssr_write: ((chunk: string) => void) | undefined;\n\ninterface ServerSideRenderer {\n /** Buffered render — resolves the whole page to a single HTML string. */\n renderFn: (payload: string | undefined) => Promise<string>;\n /** Streaming render — flushes each HTML chunk via `__ssr_write`. */\n renderStream: (payload: string | undefined) => Promise<void>;\n}\n\nexport function serverSideRendering(routeTree: RouteTree): ServerSideRenderer {\n // Build the React element for a request. Shared by the buffered and streaming\n // entry points so they render exactly the same tree.\n //\n // Async because the matched route's code (page + wrapping layouts) is\n // preloaded first: routes are `React.lazy`-wrapped, and rendering one that\n // hasn't loaded suspends its boundary, which pushes the page content into an\n // out-of-order late chunk (empty shell paints first — a visible flash on\n // every cold load). In the bundled SSR output the dynamic imports are\n // inlined, so this resolves in a microtask.\n const element = async (payload: string | undefined): Promise<JSX.Element> => {\n const serverPayload = (payload ? JSON.parse(payload) : {}) as ServerPayload;\n // Mirror the browser global on the SSR V8 runtime so `getEnv` works during\n // server rendering too. Env is process-global, so this is stable across\n // requests sharing an isolate; only set when the project exposes public vars.\n if (serverPayload.publicEnv !== undefined) {\n (globalThis as Record<string, unknown>)[PUBLIC_ENV_VARIABLE_NAME] =\n serverPayload.publicEnv;\n }\n const router = createRouter({ routeTree });\n await preloadRouteChain(router, serverPayload.location?.pathname);\n return (\n // `rawServerPayload` is the exact JSON Rust already produced; passing it\n // lets `OssidoScripts` embed it verbatim instead of re-stringifying the\n // parsed payload inside V8.\n <OssidoEntryPoint\n router={router}\n serverPayload={serverPayload}\n rawServerPayload={payload}\n />\n );\n };\n\n return {\n /**\n * Buffered render: resolve the whole page to a single string. Used for\n * error pages, static export (SSG), `catch_all`, and the dev fallback —\n * anywhere the caller needs the complete HTML up front.\n */\n async renderFn(payload: string | undefined): Promise<string> {\n // `prerender` (react-dom/static) is the SSG-correct API: its promise\n // resolves from React's `onAllReady`, so `prelude` is the fully-settled\n // tree (every Suspense boundary resolved) — no `allReady` dance and no\n // risk of capturing a fallback. Requires the SSR event loop (macrotasks)\n // to settle, same as `renderToReadableStream`.\n const { prelude } = await prerender(await element(payload));\n return await streamToString(\n prelude as unknown as ReadableStream<Uint8Array>,\n );\n },\n\n /**\n * Streaming render: flush each HTML chunk to Rust via\n * `__ssr_write` as React produces it, so the shell reaches the\n * client without waiting for the full page. `renderToReadableStream`\n * rejects on a *shell* error before any chunk is written, so Rust can still\n * send a 500 instead of a partial 200 in that case.\n */\n async renderStream(payload: string | undefined): Promise<void> {\n const write = __ssr_write;\n if (typeof write !== 'function') {\n throw new Error('__ssr_write is not registered by the runtime');\n }\n\n const stream = await renderToReadableStream(await element(payload));\n\n const streamer = createUtf8Streamer();\n for await (const chunk of stream as unknown as ReadableStream<Uint8Array>) {\n const text = streamer.push(chunk);\n if (text) write(text);\n }\n const tail = streamer.flush();\n if (tail) write(tail);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;CAqCC,SACC,QAA4D,CAAC,GACvD;CACN,MAAM,oBAAoB,MAAM,qBAAqB;AACvD,EAAC,OAAM;AAkCP,SAAgB,oBAAoB,WAA0C;CAU5E,MAAM,UAAU,OAAO,YAAsD;EAC3E,MAAM,gBAAiB,UAAU,KAAK,MAAM,OAAO,IAAI,CAAC;EAIxD,IAAI,cAAc,cAAc,QAC9B,AAAC,WAAuC,4BACtC,cAAc;EAElB,MAAM,SAAS,aAAa,EAAE,UAAU,CAAC;EACzC,MAAM,kBAAkB,QAAQ,cAAc,UAAU,QAAQ;EAChE,OAIE,oBAAC,kBAAD;GACU;GACO;GACf,kBAAkB;EACnB;CAEL;CAEA,OAAO;;;;;;EAML,MAAM,SAAS,SAA8C;GAM3D,MAAM,EAAE,YAAY,MAAM,UAAU,MAAM,QAAQ,OAAO,CAAC;GAC1D,OAAO,MAAM,eACX,OACF;EACF;;;;;;;;EASA,MAAM,aAAa,SAA4C;GAC7D,MAAM,QAAQ;GACd,IAAI,OAAO,UAAU,YACnB,MAAM,IAAI,MAAM,8CAA8C;GAGhE,MAAM,SAAS,MAAM,uBAAuB,MAAM,QAAQ,OAAO,CAAC;GAElE,MAAM,WAAW,mBAAmB;GACpC,WAAW,MAAM,SAAS,QAAiD;IACzE,MAAM,OAAO,SAAS,KAAK,KAAK;IAChC,IAAI,MAAM,MAAM,IAAI;GACtB;GACA,MAAM,OAAO,SAAS,MAAM;GAC5B,IAAI,MAAM,MAAM,IAAI;EACtB;CACF;AACF"}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -19,6 +19,12 @@ export type ServerPayload<TData = unknown> = {
|
|
|
19
19
|
data: TData;
|
|
20
20
|
/** Wrapping `layout.rs` handlers' data, keyed by each layout's `dataKey`. */
|
|
21
21
|
layoutData?: Record<string, unknown>;
|
|
22
|
+
/**
|
|
23
|
+
* The project's `#[public]` environment variables. Present only when the
|
|
24
|
+
* project defines an `#[ossido::Environment]` struct. Lifted into the
|
|
25
|
+
* `__OSSIDO_PUBLIC_ENV__` global that powers `getEnv`.
|
|
26
|
+
*/
|
|
27
|
+
publicEnv?: Record<string, unknown>;
|
|
22
28
|
/** Present (dev only) when the route's Rust handler panicked. */
|
|
23
29
|
serverError?: ServerErrorPayload;
|
|
24
30
|
} & ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossido-labs/ossido",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
"types": "./dist/esm/config/index.d.ts",
|
|
47
47
|
"default": "./dist/esm/config/index.js"
|
|
48
48
|
},
|
|
49
|
+
"./env": {
|
|
50
|
+
"types": "./dist/esm/env/index.d.ts",
|
|
51
|
+
"default": "./dist/esm/env/index.js"
|
|
52
|
+
},
|
|
49
53
|
"./ssr": {
|
|
50
54
|
"types": "./dist/esm/ssr/index.d.ts",
|
|
51
55
|
"default": "./dist/esm/ssr/index.js"
|
|
@@ -80,10 +84,12 @@
|
|
|
80
84
|
"dependencies": {
|
|
81
85
|
"@rollup/plugin-inject": "^5.0.5",
|
|
82
86
|
"@vitejs/plugin-react-swc": "^4.3.2",
|
|
87
|
+
"browserslist": "^4.28.8",
|
|
83
88
|
"fast-text-encoding": "^1.0.6",
|
|
84
|
-
"
|
|
85
|
-
"@ossido-labs/ossido-
|
|
86
|
-
"@ossido-labs/ossido-
|
|
89
|
+
"lightningcss": "^1.33.0",
|
|
90
|
+
"@ossido-labs/ossido-react-vite-plugin": "0.1.4",
|
|
91
|
+
"@ossido-labs/ossido-router": "0.1.4",
|
|
92
|
+
"@ossido-labs/ossido-ui": "0.1.4",
|
|
87
93
|
"url-search-params-polyfill": "^8.2.5",
|
|
88
94
|
"web-streams-polyfill": "^4.0.0"
|
|
89
95
|
},
|