@ossido-labs/ossido 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +28 -0
  3. package/bin/build-config.js +5 -0
  4. package/bin/build-prod.js +5 -0
  5. package/bin/dev-ssr.js +5 -0
  6. package/bin/run-build-hook.js +6 -0
  7. package/bin/watch.js +5 -0
  8. package/dist/esm/build/config/create-json-config.d.ts +10 -0
  9. package/dist/esm/build/config/create-json-config.js +52 -0
  10. package/dist/esm/build/config/create-json-config.js.map +1 -0
  11. package/dist/esm/build/config/index.d.ts +2 -0
  12. package/dist/esm/build/config/load-config.d.ts +2 -0
  13. package/dist/esm/build/config/load-config.js +20 -0
  14. package/dist/esm/build/config/load-config.js.map +1 -0
  15. package/dist/esm/build/config/normalize-config.d.ts +10 -0
  16. package/dist/esm/build/config/normalize-config.js +75 -0
  17. package/dist/esm/build/config/normalize-config.js.map +1 -0
  18. package/dist/esm/build/constants.d.ts +5 -0
  19. package/dist/esm/build/constants.js +10 -0
  20. package/dist/esm/build/constants.js.map +1 -0
  21. package/dist/esm/build/index.d.ts +15 -0
  22. package/dist/esm/build/index.js +245 -0
  23. package/dist/esm/build/index.js.map +1 -0
  24. package/dist/esm/build/logger.d.ts +11 -0
  25. package/dist/esm/build/logger.js +76 -0
  26. package/dist/esm/build/logger.js.map +1 -0
  27. package/dist/esm/build/types.d.ts +29 -0
  28. package/dist/esm/build/utils.d.ts +6 -0
  29. package/dist/esm/build/utils.js +40 -0
  30. package/dist/esm/build/utils.js.map +1 -0
  31. package/dist/esm/build-client/index.d.ts +1 -0
  32. package/dist/esm/build-client/index.js +1 -0
  33. package/dist/esm/client/index.d.ts +92 -0
  34. package/dist/esm/client/index.js +38 -0
  35. package/dist/esm/client/index.js.map +1 -0
  36. package/dist/esm/config/index.d.ts +1 -0
  37. package/dist/esm/config/index.js +0 -0
  38. package/dist/esm/config/types.d.ts +130 -0
  39. package/dist/esm/constants.d.ts +1 -0
  40. package/dist/esm/constants.js +6 -0
  41. package/dist/esm/constants.js.map +1 -0
  42. package/dist/esm/hydration/browserLogForwarding.d.ts +7 -0
  43. package/dist/esm/hydration/browserLogForwarding.js +152 -0
  44. package/dist/esm/hydration/browserLogForwarding.js.map +1 -0
  45. package/dist/esm/hydration/index.d.ts +4 -0
  46. package/dist/esm/hydration/index.js +24 -0
  47. package/dist/esm/hydration/index.js.map +1 -0
  48. package/dist/esm/index.d.ts +6 -0
  49. package/dist/esm/index.js +7 -0
  50. package/dist/esm/shared/DevResources.d.ts +7 -0
  51. package/dist/esm/shared/DevResources.js +45 -0
  52. package/dist/esm/shared/DevResources.js.map +1 -0
  53. package/dist/esm/shared/OssidoContext.d.ts +14 -0
  54. package/dist/esm/shared/OssidoContext.js +28 -0
  55. package/dist/esm/shared/OssidoContext.js.map +1 -0
  56. package/dist/esm/shared/OssidoEntryPoint.d.ts +11 -0
  57. package/dist/esm/shared/OssidoEntryPoint.js +17 -0
  58. package/dist/esm/shared/OssidoEntryPoint.js.map +1 -0
  59. package/dist/esm/shared/OssidoScripts.d.ts +2 -0
  60. package/dist/esm/shared/OssidoScripts.js +34 -0
  61. package/dist/esm/shared/OssidoScripts.js.map +1 -0
  62. package/dist/esm/shared/ProdResources.d.ts +7 -0
  63. package/dist/esm/shared/ProdResources.js +18 -0
  64. package/dist/esm/shared/ProdResources.js.map +1 -0
  65. package/dist/esm/shared/RouterContextProviderWrapper.d.ts +14 -0
  66. package/dist/esm/shared/RouterContextProviderWrapper.js +27 -0
  67. package/dist/esm/shared/RouterContextProviderWrapper.js.map +1 -0
  68. package/dist/esm/shared/dynamic/RouteLazyLoading.d.ts +6 -0
  69. package/dist/esm/shared/dynamic/RouteLazyLoading.js +20 -0
  70. package/dist/esm/shared/dynamic/RouteLazyLoading.js.map +1 -0
  71. package/dist/esm/shared/dynamic/dynamic.d.ts +15 -0
  72. package/dist/esm/shared/dynamic/dynamic.js +57 -0
  73. package/dist/esm/shared/dynamic/dynamic.js.map +1 -0
  74. package/dist/esm/shared/dynamic/index.d.ts +2 -0
  75. package/dist/esm/shared/ossido-context.d.ts +22 -0
  76. package/dist/esm/shared/ossido-context.js +22 -0
  77. package/dist/esm/shared/ossido-context.js.map +1 -0
  78. package/dist/esm/shared/routeHot.d.ts +10 -0
  79. package/dist/esm/shared/routeHot.js +20 -0
  80. package/dist/esm/shared/routeHot.js.map +1 -0
  81. package/dist/esm/ssr/index.d.ts +4 -0
  82. package/dist/esm/ssr/index.js +6 -0
  83. package/dist/esm/ssr/polyfills/Event.d.ts +18 -0
  84. package/dist/esm/ssr/polyfills/Event.js +24 -0
  85. package/dist/esm/ssr/polyfills/Event.js.map +1 -0
  86. package/dist/esm/ssr/polyfills/MessageChannel.d.ts +19 -0
  87. package/dist/esm/ssr/polyfills/MessageChannel.js +50 -0
  88. package/dist/esm/ssr/polyfills/MessageChannel.js.map +1 -0
  89. package/dist/esm/ssr/polyfills/MessageEvent.d.ts +12 -0
  90. package/dist/esm/ssr/polyfills/MessageEvent.js +20 -0
  91. package/dist/esm/ssr/polyfills/MessageEvent.js.map +1 -0
  92. package/dist/esm/ssr/polyfills/globalScope.d.ts +30 -0
  93. package/dist/esm/ssr/polyfills/globalScope.js +5 -0
  94. package/dist/esm/ssr/polyfills/globalScope.js.map +1 -0
  95. package/dist/esm/ssr/server.d.ts +40 -0
  96. package/dist/esm/ssr/server.js +61 -0
  97. package/dist/esm/ssr/server.js.map +1 -0
  98. package/dist/esm/ssr/utils.d.ts +19 -0
  99. package/dist/esm/ssr/utils.js +78 -0
  100. package/dist/esm/ssr/utils.js.map +1 -0
  101. package/dist/esm/types.d.ts +34 -0
  102. package/package.json +110 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chris Schofield
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # ossido
2
+
3
+ The core JavaScript runtime for [Ossido](https://ossido.dev) — the React/Rust
4
+ full-stack framework.
5
+
6
+ This package is installed into every scaffolded Ossido app. It provides the
7
+ client entry/hydration, the server-side rendering entry, the build tooling, and
8
+ the config types the framework relies on. You normally don't import it directly
9
+ — the [`ossido` CLI](https://github.com/ossido-labs/ossido) and the framework's
10
+ generated code wire it up for you.
11
+
12
+ ## Getting started
13
+
14
+ Create a new project with the CLI (installs this package for you):
15
+
16
+ ```sh
17
+ ossido new [PROJECT_NAME]
18
+ ```
19
+
20
+ ## Entry points
21
+
22
+ - `ossido` — framework primitives (e.g. `Link`) used in app code
23
+ - `ossido/config` — `OssidoConfig` type and config helpers
24
+ - `ossido/client` / `ossido/hydration` — client bootstrap + hydration
25
+ - `ossido/ssr` — server-side rendering entry
26
+ - `ossido/build` / `ossido/build-client` — build pipeline
27
+
28
+ Check [ossido](https://github.com/ossido-labs/ossido) for more.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { buildConfig } from '../dist/esm/build/index.js';
4
+
5
+ buildConfig();
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { buildProd } from '../dist/esm/build/index.js';
4
+
5
+ buildProd();
package/bin/dev-ssr.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { developmentSSRBundle } from '../dist/esm/build/index.js';
4
+
5
+ developmentSSRBundle();
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runBuildHook } from '../dist/esm/build/index.js';
4
+
5
+ // Invoked by the Rust CLI as `ossido-run-build-hook <prebuild|postbuild>`.
6
+ runBuildHook(process.argv[2]);
package/bin/watch.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { developmentCSRWatch } from '../dist/esm/build/index.js';
4
+
5
+ developmentCSRWatch();
@@ -0,0 +1,10 @@
1
+ import type { InternalOssidoConfig } from '../types';
2
+ /**
3
+ * This function creates a JSON config file for the server,
4
+ * that will be shared with the client as a prop.
5
+ * The file will be saved at`.ossido/config/config.json`.
6
+ *
7
+ * The file is in JSON format to ensure it's easily read by the server,
8
+ * which is written in Rust.
9
+ */
10
+ export declare function createJsonConfig(config: InternalOssidoConfig): Promise<void>;
@@ -0,0 +1,52 @@
1
+ import { CONFIG_FOLDER_NAME, DOT_OSSIDO_FOLDER_NAME, SERVER_CONFIG_NAME } from "../constants.js";
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+
5
+ //#region src/build/config/create-json-config.ts
6
+ const CONFIG_PATH = path.join(DOT_OSSIDO_FOLDER_NAME, CONFIG_FOLDER_NAME, SERVER_CONFIG_NAME);
7
+ /**
8
+ * Strip the build-time-only, non-serializable properties before the config is
9
+ * written to JSON for the Rust server/client: `vite` (plugins are functions) and
10
+ * `build` (the `prebuild`/`postbuild` hooks are functions). Both would otherwise
11
+ * fail `structuredClone`, and neither is needed by the server/client — the hooks
12
+ * are invoked from the JS build via `loadConfig` (which reads the transpiled
13
+ * config, keeping the functions).
14
+ */
15
+ function removeNonSerializableProperties(config) {
16
+ /**
17
+ * Using {@link structuredClone} cause the following errors based on runtime env:
18
+ * ```text
19
+ * node
20
+ * DOMException [DataCloneError]: configureServer(s){a.push(s)} could not be cloned.
21
+ *
22
+ * vitest
23
+ * DataCloneError: (id) => id === runtimePublicPath ? id : void 0 could not be cloned.
24
+ * ```
25
+ * when vite plugins are passed inside to the config.
26
+ *
27
+ * Since the purpose of this function is to remove the vite object
28
+ * we are going to use destructing rather than {@link structuredClone} and `delete`
29
+ *
30
+ * @see https://github.com/tuono-labs/tuono/issues/414
31
+ */
32
+ const { vite, build, ...configRest } = config;
33
+ return structuredClone(configRest);
34
+ }
35
+ /**
36
+ * This function creates a JSON config file for the server,
37
+ * that will be shared with the client as a prop.
38
+ * The file will be saved at`.ossido/config/config.json`.
39
+ *
40
+ * The file is in JSON format to ensure it's easily read by the server,
41
+ * which is written in Rust.
42
+ */
43
+ async function createJsonConfig(config) {
44
+ const jsonConfig = removeNonSerializableProperties(config);
45
+ const fullPath = path.resolve(CONFIG_PATH);
46
+ const jsonContent = JSON.stringify(jsonConfig);
47
+ await fs.writeFile(fullPath, jsonContent, "utf-8");
48
+ }
49
+
50
+ //#endregion
51
+ export { createJsonConfig };
52
+ //# sourceMappingURL=create-json-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-json-config.js","names":[],"sources":["../../../../src/build/config/create-json-config.ts"],"sourcesContent":["import fs from 'fs/promises';\nimport path from 'path';\n\nimport type { InternalOssidoConfig } from '../types';\n\nimport {\n DOT_OSSIDO_FOLDER_NAME,\n CONFIG_FOLDER_NAME,\n SERVER_CONFIG_NAME,\n} from '../constants';\n\nconst CONFIG_PATH = path.join(\n DOT_OSSIDO_FOLDER_NAME,\n CONFIG_FOLDER_NAME,\n SERVER_CONFIG_NAME,\n);\n/**\n * Strip the build-time-only, non-serializable properties before the config is\n * written to JSON for the Rust server/client: `vite` (plugins are functions) and\n * `build` (the `prebuild`/`postbuild` hooks are functions). Both would otherwise\n * fail `structuredClone`, and neither is needed by the server/client — the hooks\n * are invoked from the JS build via `loadConfig` (which reads the transpiled\n * config, keeping the functions).\n */\nfunction removeNonSerializableProperties(\n config: InternalOssidoConfig,\n): Omit<InternalOssidoConfig, 'vite' | 'build'> {\n /**\n * Using {@link structuredClone} cause the following errors based on runtime env:\n * ```text\n * node\n * DOMException [DataCloneError]: configureServer(s){a.push(s)} could not be cloned.\n *\n * vitest\n * DataCloneError: (id) => id === runtimePublicPath ? id : void 0 could not be cloned.\n * ```\n * when vite plugins are passed inside to the config.\n *\n * Since the purpose of this function is to remove the vite object\n * we are going to use destructing rather than {@link structuredClone} and `delete`\n *\n * @see https://github.com/tuono-labs/tuono/issues/414\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { vite, build, ...configRest } = config;\n return structuredClone(configRest);\n}\n\n/**\n * This function creates a JSON config file for the server,\n * that will be shared with the client as a prop.\n * The file will be saved at`.ossido/config/config.json`.\n *\n * The file is in JSON format to ensure it's easily read by the server,\n * which is written in Rust.\n */\nexport async function createJsonConfig(\n config: InternalOssidoConfig,\n): Promise<void> {\n const jsonConfig = removeNonSerializableProperties(config);\n\n const fullPath = path.resolve(CONFIG_PATH);\n const jsonContent = JSON.stringify(jsonConfig);\n\n // No need to manage error state. Ossido CLI will manage it.\n await fs.writeFile(fullPath, jsonContent, 'utf-8');\n}\n"],"mappings":";;;;;AAWA,MAAM,cAAc,KAAK,KACvB,wBACA,oBACA,kBACF;;;;;;;;;AASA,SAAS,gCACP,QAC8C;;;;;;;;;;;;;;;;;CAkB9C,MAAM,EAAE,MAAM,OAAO,GAAG,eAAe;CACvC,OAAO,gBAAgB,UAAU;AACnC;;;;;;;;;AAUA,eAAsB,iBACpB,QACe;CACf,MAAM,aAAa,gCAAgC,MAAM;CAEzD,MAAM,WAAW,KAAK,QAAQ,WAAW;CACzC,MAAM,cAAc,KAAK,UAAU,UAAU;CAG7C,MAAM,GAAG,UAAU,UAAU,aAAa,OAAO;AACnD"}
@@ -0,0 +1,2 @@
1
+ export { loadConfig } from './load-config';
2
+ export { createJsonConfig } from './create-json-config';
@@ -0,0 +1,2 @@
1
+ import type { InternalOssidoConfig } from '../types';
2
+ export declare const loadConfig: () => Promise<InternalOssidoConfig>;
@@ -0,0 +1,20 @@
1
+ import { CONFIG_FILE_NAME, CONFIG_FOLDER_NAME, DOT_OSSIDO_FOLDER_NAME } from "../constants.js";
2
+ import { normalizeConfig } from "./normalize-config.js";
3
+ import path from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+
6
+ //#region src/build/config/load-config.ts
7
+ const loadConfig = async () => {
8
+ try {
9
+ const configFile = await import(pathToFileURL(path.join(process.cwd(), DOT_OSSIDO_FOLDER_NAME, CONFIG_FOLDER_NAME, CONFIG_FILE_NAME)).href);
10
+ return normalizeConfig(configFile.default);
11
+ } catch (err) {
12
+ console.error("Failed to load ossido.config.ts");
13
+ console.error(err);
14
+ return {};
15
+ }
16
+ };
17
+
18
+ //#endregion
19
+ export { loadConfig };
20
+ //# sourceMappingURL=load-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-config.js","names":[],"sources":["../../../../src/build/config/load-config.ts"],"sourcesContent":["import path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { OssidoConfig } from '../../config';\n\nimport type { InternalOssidoConfig } from '../types';\n\nimport {\n DOT_OSSIDO_FOLDER_NAME,\n CONFIG_FOLDER_NAME,\n CONFIG_FILE_NAME,\n} from '../constants';\n\nimport { normalizeConfig } from './normalize-config';\n\nexport const loadConfig = async (): Promise<InternalOssidoConfig> => {\n try {\n const configFile = (await import(\n pathToFileURL(\n path.join(\n process.cwd(),\n DOT_OSSIDO_FOLDER_NAME,\n CONFIG_FOLDER_NAME,\n CONFIG_FILE_NAME,\n ),\n ).href\n )) as { default: OssidoConfig };\n\n return normalizeConfig(configFile.default);\n } catch (err) {\n console.error('Failed to load ossido.config.ts');\n console.error(err);\n return {} as InternalOssidoConfig;\n }\n};\n"],"mappings":";;;;;;AAeA,MAAa,aAAa,YAA2C;CACnE,IAAI;EACF,MAAM,aAAc,MAAM,OACxB,cACE,KAAK,KACH,QAAQ,IAAI,GACZ,wBACA,oBACA,gBACF,CACF,CAAC,CAAC;EAGJ,OAAO,gBAAgB,WAAW,OAAO;CAC3C,SAAS,KAAK;EACZ,QAAQ,MAAM,iCAAiC;EAC/C,QAAQ,MAAM,GAAG;EACjB,OAAO,CAAC;CACV;AACF"}
@@ -0,0 +1,10 @@
1
+ import type { OssidoConfig } from '../../config';
2
+ import type { InternalOssidoConfig } from '../types';
3
+ /**
4
+ * Wrapper function to normalize the ossido.config.ts file
5
+ *
6
+ * @warning Exported for unit test.
7
+ * There is no easy way to mock the module export and change it in every test
8
+ * and also testing the error
9
+ */
10
+ export declare const normalizeConfig: (config: OssidoConfig) => InternalOssidoConfig;
@@ -0,0 +1,75 @@
1
+ import { CONFIG_FOLDER_NAME, DOT_OSSIDO_FOLDER_NAME } from "../constants.js";
2
+ import path from "node:path";
3
+
4
+ //#region src/build/config/normalize-config.ts
5
+ /**
6
+ * Normalize vite alias option:
7
+ * - If the path starts with `src` folder, transform it to absolute, prepending the ossido root folder
8
+ * - If the path is absolute, remove the ".ossido/config/" path from it
9
+ * - Otherwise leave the path untouched
10
+ */
11
+ const normalizeAliasPath = (aliasPath) => {
12
+ if (aliasPath.startsWith("./src") || aliasPath.startsWith("src")) return path.join(process.cwd(), aliasPath);
13
+ if (path.isAbsolute(aliasPath)) return aliasPath.replace(path.join(DOT_OSSIDO_FOLDER_NAME, CONFIG_FOLDER_NAME), "");
14
+ return aliasPath;
15
+ };
16
+ /**
17
+ * From a given vite aliasOptions apply {@link normalizeAliasPath} for each alias.
18
+ *
19
+ * The config is bundled by `vite` and emitted inside {@link DOT_OSSIDO_FOLDER_NAME}/{@link CONFIG_FOLDER_NAME}.
20
+ * According to this, we have to ensure that the aliases provided by the user are updated to refer to the right folders.
21
+ *
22
+ * @see https://github.com/Valerioageno/tuono/pull/153#issuecomment-2508142877
23
+ */
24
+ const normalizeViteAlias = (alias) => {
25
+ if (!alias) return;
26
+ if (Array.isArray(alias)) return alias.map(({ replacement, ...userAliasDefinition }) => ({
27
+ ...userAliasDefinition,
28
+ replacement: normalizeAliasPath(replacement)
29
+ }));
30
+ if (typeof alias === "object") {
31
+ const normalizedAlias = {};
32
+ for (const [key, value] of Object.entries(alias)) normalizedAlias[key] = normalizeAliasPath(value);
33
+ return normalizedAlias;
34
+ }
35
+ return alias;
36
+ };
37
+ /**
38
+ * Wrapper function to normalize the ossido.config.ts file
39
+ *
40
+ * @warning Exported for unit test.
41
+ * There is no easy way to mock the module export and change it in every test
42
+ * and also testing the error
43
+ */
44
+ const normalizeConfig = (config) => {
45
+ return {
46
+ server: {
47
+ host: config.server?.host ?? "localhost",
48
+ origin: config.server?.origin ?? null,
49
+ port: config.server?.port ?? 3e3
50
+ },
51
+ vite: {
52
+ alias: normalizeViteAlias(config.vite?.alias),
53
+ css: config.vite?.css,
54
+ optimizeDeps: config.vite?.optimizeDeps,
55
+ plugins: config.vite?.plugins ?? []
56
+ },
57
+ logging: {
58
+ format: config.logging?.format ?? "pretty",
59
+ routeTree: config.logging?.routeTree ?? false,
60
+ browser: {
61
+ enabled: config.logging?.browser?.enabled ?? true,
62
+ level: config.logging?.browser?.level ?? "info"
63
+ }
64
+ },
65
+ dev: { criticalCss: config.dev?.criticalCss ?? true },
66
+ ssr: { renderThreads: config.ssr?.renderThreads ?? null },
67
+ output: config.output ?? "server",
68
+ viewTransitions: config.viewTransitions ?? false,
69
+ ...config.build ? { build: config.build } : {}
70
+ };
71
+ };
72
+
73
+ //#endregion
74
+ export { normalizeConfig };
75
+ //# sourceMappingURL=normalize-config.js.map
@@ -0,0 +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;EAI3C,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;CAChD;AACF"}
@@ -0,0 +1,5 @@
1
+ export declare const DOT_OSSIDO_FOLDER_NAME = ".ossido";
2
+ export declare const CONFIG_FOLDER_NAME = "config";
3
+ export declare const CONFIG_FILE_NAME = "config.mjs";
4
+ export declare const SERVER_CONFIG_NAME = "config.json";
5
+ export declare const ENV_PREFIX = "OSSIDO_PUBLIC_";
@@ -0,0 +1,10 @@
1
+ //#region src/build/constants.ts
2
+ const DOT_OSSIDO_FOLDER_NAME = ".ossido";
3
+ const CONFIG_FOLDER_NAME = "config";
4
+ const CONFIG_FILE_NAME = "config.mjs";
5
+ const SERVER_CONFIG_NAME = "config.json";
6
+ const ENV_PREFIX = "OSSIDO_PUBLIC_";
7
+
8
+ //#endregion
9
+ export { CONFIG_FILE_NAME, CONFIG_FOLDER_NAME, DOT_OSSIDO_FOLDER_NAME, ENV_PREFIX, SERVER_CONFIG_NAME };
10
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../../../src/build/constants.ts"],"sourcesContent":["export const DOT_OSSIDO_FOLDER_NAME = '.ossido';\nexport const CONFIG_FOLDER_NAME = 'config';\nexport const CONFIG_FILE_NAME = 'config.mjs';\nexport const SERVER_CONFIG_NAME = 'config.json';\nexport const ENV_PREFIX = 'OSSIDO_PUBLIC_';\n"],"mappings":";AAAA,MAAa,yBAAyB;AACtC,MAAa,qBAAqB;AAClC,MAAa,mBAAmB;AAChC,MAAa,qBAAqB;AAClC,MAAa,aAAa"}
@@ -0,0 +1,15 @@
1
+ declare const developmentSSRBundle: () => void;
2
+ declare const developmentCSRWatch: () => void;
3
+ declare const buildProd: () => void;
4
+ declare const buildConfig: () => void;
5
+ /**
6
+ * Run a user-defined build lifecycle hook (`prebuild` / `postbuild`) from
7
+ * `ossido.config.ts`. Invoked by the Rust CLI (`ossido-run-build-hook <name>`)
8
+ * at the matching point of `ossido build`. No-ops when the hook is undefined.
9
+ *
10
+ * The output/public dirs and mode mirror what the build produces: static builds
11
+ * deploy `out/static`, server builds `out`. `postbuild` gets the emitted-file
12
+ * manifest; `prebuild` runs before anything is emitted, so its manifest is empty.
13
+ */
14
+ declare const runBuildHook: (name: "prebuild" | "postbuild") => void;
15
+ export { buildProd, buildConfig, developmentCSRWatch, developmentSSRBundle, runBuildHook, };
@@ -0,0 +1,245 @@
1
+ import { blockingAsync, listFilesRecursive } from "./utils.js";
2
+ import { ENV_PREFIX } from "./constants.js";
3
+ import { loadConfig } from "./config/load-config.js";
4
+ import { createJsonConfig } from "./config/create-json-config.js";
5
+ import { createOssidoViteLogger } from "./logger.js";
6
+ import { createRequire } from "node:module";
7
+ import { join, resolve } from "node:path";
8
+ import { readFileSync, readdirSync } from "node:fs";
9
+ import { gzipSync } from "node:zlib";
10
+ import { build, createServer, mergeConfig } from "vite";
11
+ import react from "@vitejs/plugin-react-swc";
12
+ import inject from "@rollup/plugin-inject";
13
+ import { OssidoReactPlugin, routeGenerator } from "@ossido-labs/ossido-react-vite-plugin";
14
+ import { ErrorOverlayVitePlugin } from "@ossido-labs/ossido-ui/vite-plugin";
15
+
16
+ //#region src/build/index.ts
17
+ const require = createRequire(import.meta.url);
18
+ /**
19
+ * Absolute entry paths for the generated `.ossido` scaffold.
20
+ *
21
+ * The vite config sets `root: '.ossido'`. Vite's `build()` resolves a relative
22
+ * `rollupOptions.input` against the CWD, but the dev server's dependency
23
+ * scanner (rolldown, vite 8+) resolves it against `root` — so a path like
24
+ * `./.ossido/client-main.tsx` double-nests to `.ossido/.ossido/...` and fails to
25
+ * resolve. Absolute paths are unambiguous across both code paths.
26
+ */
27
+ const CLIENT_MAIN_ENTRY = resolve(".ossido/client-main.tsx");
28
+ const SERVER_MAIN_ENTRY = resolve(".ossido/server-main.tsx");
29
+ /**
30
+ * `@rollup/plugin-inject` injects these imports into every module that
31
+ * references the globals below — including third-party files deep in
32
+ * `node_modules` (e.g. `react-dom/server`). Vite 8's bundler (rolldown)
33
+ * resolves an injected bare specifier relative to the *importing* file, so
34
+ * `'@ossido-labs/ossido/ssr'` fails to resolve from within `react-dom`. Resolving to
35
+ * absolute paths up-front makes the injected imports resolvable from anywhere
36
+ * (and is a no-op for rollup on Vite <= 7).
37
+ */
38
+ const SSR_POLYFILLS_MODULE = require.resolve("@ossido-labs/ossido/ssr");
39
+ const WEB_STREAMS_POLYFILL_MODULE = require.resolve("web-streams-polyfill");
40
+ const VITE_SSR_PLUGINS = [{
41
+ enforce: "post",
42
+ ...inject({
43
+ ReadableStream: [WEB_STREAMS_POLYFILL_MODULE, "ReadableStream"],
44
+ /**
45
+ * Added to support `react@19`
46
+ * @see https://github.com/tuono-labs/tuono/issues/218
47
+ */
48
+ MessageChannel: [SSR_POLYFILLS_MODULE, "MessageChannelPolyfill"],
49
+ MessageEvent: [SSR_POLYFILLS_MODULE, "MessageEventPolyfill"],
50
+ Event: [SSR_POLYFILLS_MODULE, "EventPolyfill"]
51
+ })
52
+ }];
53
+ /**
54
+ * From a resolved {@link InternalOssidoConfig} return a `vite` "mergeable"
55
+ * {@link InlineConfig} including all default ossido related options
56
+ */
57
+ function createBaseViteConfigFromOssidoConfig(ossidoConfig) {
58
+ /**
59
+ * @warning Keep in sync with {@link LazyLoadingPlugin} tests:
60
+ * packages/lazy-fn-vite-plugin/tests/transpileSource.test.ts
61
+ */
62
+ const pluginFilesInclude = /\.(jsx|js|mdx|md|tsx|ts)$/;
63
+ return {
64
+ root: ".ossido",
65
+ logLevel: "error",
66
+ customLogger: createOssidoViteLogger("error"),
67
+ publicDir: "../public",
68
+ cacheDir: "cache",
69
+ envDir: "../",
70
+ envPrefix: ENV_PREFIX,
71
+ define: {
72
+ __OSSIDO_STATIC__: JSON.stringify(process.env.OSSIDO_STATIC === "true"),
73
+ __OSSIDO_CRITICAL_CSS__: JSON.stringify(ossidoConfig.dev?.criticalCss ?? true),
74
+ __OSSIDO_VIEW_TRANSITIONS__: JSON.stringify(ossidoConfig.viewTransitions ?? false)
75
+ },
76
+ resolve: { alias: ossidoConfig.vite?.alias ?? {} },
77
+ css: ossidoConfig.vite?.css,
78
+ optimizeDeps: ossidoConfig.vite?.optimizeDeps,
79
+ plugins: [
80
+ ...ossidoConfig.vite?.plugins ?? [],
81
+ react({ include: pluginFilesInclude }),
82
+ OssidoReactPlugin({ criticalCss: ossidoConfig.dev?.criticalCss ?? true })
83
+ ]
84
+ };
85
+ }
86
+ const developmentSSRBundle = () => {
87
+ blockingAsync(async () => {
88
+ const config = await loadConfig();
89
+ await build(mergeConfig(createBaseViteConfigFromOssidoConfig(config), {
90
+ cacheDir: "cache-ssr",
91
+ plugins: VITE_SSR_PLUGINS,
92
+ build: {
93
+ ssr: true,
94
+ minify: false,
95
+ outDir: "server",
96
+ emptyOutDir: true,
97
+ rollupOptions: {
98
+ input: SERVER_MAIN_ENTRY,
99
+ output: {
100
+ entryFileNames: "dev-server.js",
101
+ format: "es"
102
+ }
103
+ }
104
+ },
105
+ ssr: {
106
+ target: "webworker",
107
+ noExternal: true
108
+ }
109
+ }));
110
+ });
111
+ };
112
+ const developmentCSRWatch = () => {
113
+ blockingAsync(async () => {
114
+ const config = await loadConfig();
115
+ await (await createServer(mergeConfig(createBaseViteConfigFromOssidoConfig(config), {
116
+ base: "/vite-server/",
117
+ plugins: [ErrorOverlayVitePlugin],
118
+ server: {
119
+ host: config.server.host,
120
+ port: config.server.port + 1,
121
+ strictPort: true
122
+ },
123
+ build: {
124
+ manifest: true,
125
+ emptyOutDir: true,
126
+ rollupOptions: { input: CLIENT_MAIN_ENTRY }
127
+ }
128
+ }))).listen();
129
+ });
130
+ };
131
+ const KB = 1024;
132
+ const formatKb = (bytes) => `${(bytes / KB).toFixed(1)} kB`;
133
+ /**
134
+ * Print a compact per-asset size summary (raw + gzip) for the prod build, so
135
+ * size regressions are visible on every `ossido build` without extra tooling.
136
+ * Reads the emitted files directly — works regardless of vite's log level.
137
+ */
138
+ const printBundleSummary = () => {
139
+ const collectDir = (dir, prefix) => {
140
+ try {
141
+ return readdirSync(dir, {
142
+ recursive: true,
143
+ encoding: "utf-8"
144
+ }).filter((file) => /\.(js|css)$/.test(file)).map((file) => {
145
+ const content = readFileSync(join(dir, file));
146
+ return {
147
+ label: `${prefix}/${file}`,
148
+ size: content.length,
149
+ gzip: gzipSync(content).length
150
+ };
151
+ });
152
+ } catch {
153
+ return [];
154
+ }
155
+ };
156
+ const assets = [...collectDir(resolve("out/client"), "client"), ...collectDir(resolve("out/server"), "server")].sort((a, b) => b.size - a.size);
157
+ if (!assets.length) return;
158
+ const labelWidth = Math.max(...assets.map((asset) => asset.label.length));
159
+ console.log("");
160
+ for (const asset of assets) console.log(` ${asset.label.padEnd(labelWidth)} ${formatKb(asset.size).padStart(10)} │ gzip: ${formatKb(asset.gzip).padStart(9)}`);
161
+ const total = assets.reduce((sum, asset) => sum + asset.size, 0);
162
+ const totalGzip = assets.reduce((sum, asset) => sum + asset.gzip, 0);
163
+ console.log(` ${"total".padEnd(labelWidth)} ${formatKb(total).padStart(10)} │ gzip: ${formatKb(totalGzip).padStart(9)}`);
164
+ console.log("");
165
+ };
166
+ const buildProd = () => {
167
+ blockingAsync(async () => {
168
+ const config = await loadConfig();
169
+ await routeGenerator();
170
+ await Promise.all([build(mergeConfig(createBaseViteConfigFromOssidoConfig(config), { build: {
171
+ manifest: true,
172
+ emptyOutDir: true,
173
+ outDir: "../out/client",
174
+ rollupOptions: { input: CLIENT_MAIN_ENTRY }
175
+ } })), build(mergeConfig(createBaseViteConfigFromOssidoConfig(config), {
176
+ plugins: VITE_SSR_PLUGINS,
177
+ build: {
178
+ ssr: true,
179
+ minify: true,
180
+ outDir: "../out/server",
181
+ emptyOutDir: true,
182
+ rollupOptions: {
183
+ input: SERVER_MAIN_ENTRY,
184
+ output: {
185
+ entryFileNames: "prod-server.js",
186
+ format: "es"
187
+ }
188
+ }
189
+ },
190
+ ssr: {
191
+ target: "webworker",
192
+ noExternal: true
193
+ }
194
+ }))]);
195
+ printBundleSummary();
196
+ });
197
+ };
198
+ const buildConfig = () => {
199
+ blockingAsync(async () => {
200
+ await build({
201
+ root: ".ossido",
202
+ logLevel: "error",
203
+ cacheDir: "cache",
204
+ envDir: "../",
205
+ build: {
206
+ ssr: true,
207
+ outDir: "config",
208
+ emptyOutDir: true,
209
+ rollupOptions: {
210
+ input: "./ossido.config.ts",
211
+ output: { entryFileNames: "config.mjs" }
212
+ }
213
+ }
214
+ });
215
+ const config = await loadConfig();
216
+ await createJsonConfig(config);
217
+ });
218
+ };
219
+ /**
220
+ * Run a user-defined build lifecycle hook (`prebuild` / `postbuild`) from
221
+ * `ossido.config.ts`. Invoked by the Rust CLI (`ossido-run-build-hook <name>`)
222
+ * at the matching point of `ossido build`. No-ops when the hook is undefined.
223
+ *
224
+ * The output/public dirs and mode mirror what the build produces: static builds
225
+ * deploy `out/static`, server builds `out`. `postbuild` gets the emitted-file
226
+ * manifest; `prebuild` runs before anything is emitted, so its manifest is empty.
227
+ */
228
+ const runBuildHook = (name) => blockingAsync(async () => {
229
+ const config = await loadConfig();
230
+ const hook = config.build?.[name];
231
+ if (typeof hook !== "function") return;
232
+ const mode = process.env.OSSIDO_STATIC === "true" ? "static" : "server";
233
+ const outputDirectory = resolve(mode === "static" ? "out/static" : "out");
234
+ await hook({
235
+ mode,
236
+ outputDirectory,
237
+ publicDirectory: resolve("public"),
238
+ manifest: name === "postbuild" ? listFilesRecursive(outputDirectory) : [],
239
+ config
240
+ });
241
+ });
242
+
243
+ //#endregion
244
+ export { buildConfig, buildProd, developmentCSRWatch, developmentSSRBundle, runBuildHook };
245
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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 { OssidoReactPlugin, routeGenerator } 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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"}
@@ -0,0 +1,11 @@
1
+ import { type Logger, type LogLevel } from 'vite';
2
+ type FeLevel = 'INFO' | 'WARN' | 'ERROR';
3
+ /** Print a single `[FE]`-tagged log line. */
4
+ export declare function feLog(level: FeLevel, message: string): void;
5
+ /**
6
+ * A vite {@link Logger} that routes vite's own output through the `[FE]` format,
7
+ * honouring `logLevel` (vite does not pre-filter for a custom logger, so the
8
+ * logger must do it — otherwise info/warn noise leaks even at `'error'`).
9
+ */
10
+ export declare function createOssidoViteLogger(logLevel?: LogLevel): Logger;
11
+ export {};