fimo-vite 0.21.0-experimental.1

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.
@@ -0,0 +1,27 @@
1
+ // Vite HMR build-error bridge.
2
+ //
3
+ // This file is served by vite (not embedded inline) so that vite's plugin
4
+ // pipeline transforms `import.meta.hot` into a real hot-context — inline
5
+ // `<script type="module">` blocks bypass that pipeline and `import.meta.hot`
6
+ // stays `undefined`. The inline overlay runtime (overlay-runtime.ts) exposes
7
+ // its helpers on `window.__fimoOverlay`, which we call here.
8
+ function api() {
9
+ if (typeof window === 'undefined') {
10
+ return undefined;
11
+ }
12
+ return window.__fimoOverlay;
13
+ }
14
+ if (import.meta && import.meta.hot) {
15
+ import.meta.hot.on('vite:error', (data) => {
16
+ const a = api();
17
+ if (!a)
18
+ return;
19
+ const d = a.getErrorDetails(data);
20
+ a.mount();
21
+ a.notify('Build Error', d.message, d.stack);
22
+ });
23
+ import.meta.hot.on('vite:afterUpdate', () => {
24
+ api()?.unmount();
25
+ });
26
+ }
27
+ export {};
@@ -0,0 +1,24 @@
1
+ declare const ID = "fimo-error-overlay";
2
+ declare function ensureStyles(): void;
3
+ declare function mountBlack(): void;
4
+ declare function unmount(): void;
5
+ declare function getErrorDetails(e: unknown): {
6
+ message: string;
7
+ stack: string;
8
+ };
9
+ declare function postToTargets(type: string, payload: {
10
+ heading: string;
11
+ message: string;
12
+ stack: string;
13
+ }): void;
14
+ declare function notify(heading: string, message: string, stack: string): void;
15
+ type FimoOverlayApi = {
16
+ mount: () => void;
17
+ unmount: () => void;
18
+ notify: (heading: string, message: string, stack: string) => void;
19
+ getErrorDetails: (e: unknown) => {
20
+ message: string;
21
+ stack: string;
22
+ };
23
+ };
24
+ //# sourceMappingURL=overlay-runtime.d.ts.map
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ const ID = 'fimo-error-overlay';
3
+ function ensureStyles() {
4
+ if (document.getElementById(ID + '-styles')) {
5
+ return;
6
+ }
7
+ const s = document.createElement('style');
8
+ s.id = ID + '-styles';
9
+ s.textContent =
10
+ 'vite-error-overlay, #vite-error-overlay, vite-overlay { display: none !important; }' +
11
+ '#' +
12
+ ID +
13
+ ' { position: fixed; inset: 0; background: #000; z-index: 2147483647; display: none; }';
14
+ document.head.appendChild(s);
15
+ }
16
+ function mountBlack() {
17
+ ensureStyles();
18
+ let el = document.getElementById(ID);
19
+ if (!el) {
20
+ el = document.createElement('div');
21
+ el.id = ID;
22
+ document.body.appendChild(el);
23
+ }
24
+ el.style.display = 'block';
25
+ }
26
+ function unmount() {
27
+ const el = document.getElementById(ID);
28
+ if (el) {
29
+ el.style.display = 'none';
30
+ }
31
+ }
32
+ function getErrorDetails(e) {
33
+ try {
34
+ // Preserve the original loose extraction logic but in typed form
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ const anyErr = e && e.error ? e.error : e;
37
+ let err = null;
38
+ if (!anyErr) {
39
+ err = null;
40
+ }
41
+ else if (anyErr.error) {
42
+ err = anyErr.error;
43
+ }
44
+ else if (anyErr.reason) {
45
+ err = anyErr.reason;
46
+ }
47
+ else if (anyErr.err) {
48
+ err = anyErr.err;
49
+ }
50
+ else {
51
+ err = anyErr;
52
+ }
53
+ const message = typeof err === 'string' ? err : err && err.message ? err.message : String(err || 'Unknown error');
54
+ const stack = err && err.stack ? String(err.stack) : '';
55
+ return { message, stack };
56
+ }
57
+ catch {
58
+ return { message: 'Unknown error', stack: '' };
59
+ }
60
+ }
61
+ function postToTargets(type, payload) {
62
+ const data = { type, payload };
63
+ try {
64
+ if (window.parent && window.parent !== window) {
65
+ window.parent.postMessage(data, '*');
66
+ }
67
+ }
68
+ catch {
69
+ // ignore
70
+ }
71
+ try {
72
+ if (window.top && window.top !== window && window.top !== window.parent) {
73
+ window.top.postMessage(data, '*');
74
+ }
75
+ }
76
+ catch {
77
+ // ignore
78
+ }
79
+ }
80
+ function notify(heading, message, stack) {
81
+ const payload = { heading, message, stack };
82
+ // First, send a preview-level error so the parent can perform a minimal handshake
83
+ // and lock onto the sandbox origin in build-error scenarios.
84
+ postToTargets('preview/error', payload);
85
+ // Then, send the existing app-level error used by the UI overlay.
86
+ postToTargets('app/error', payload);
87
+ }
88
+ window.addEventListener('error', (e) => {
89
+ const d = getErrorDetails(e);
90
+ mountBlack();
91
+ notify('Runtime Error', d.message, d.stack);
92
+ });
93
+ window.addEventListener('unhandledrejection', (e) => {
94
+ const d = getErrorDetails(e);
95
+ mountBlack();
96
+ notify('Runtime Error', d.message, d.stack);
97
+ });
98
+ window.__fimoOverlay = {
99
+ mount: mountBlack,
100
+ unmount,
101
+ notify,
102
+ getErrorDetails,
103
+ };
package/dist/vite.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ import type { PluginOption } from 'vite';
2
+ export interface FimoViteOptions {
3
+ /**
4
+ * Emit `sitemap.xml` at build (and serve at `/sitemap.xml` in dev). Reads
5
+ * `seo.url` from `.fimo/config.json` as the base URL.
6
+ *
7
+ * Opt-in via `fimo({ sitemap: true })`.
8
+ */
9
+ sitemap?: boolean;
10
+ /**
11
+ * Emit `robots.txt` at build (and serve at `/robots.txt` in dev). Defaults
12
+ * to an allow-all robots policy + a `Sitemap:` directive when `seo.url` is
13
+ * set in `.fimo/config.json`.
14
+ *
15
+ * Opt-in via `fimo({ robots: true })`.
16
+ */
17
+ robots?: boolean;
18
+ /**
19
+ * Inject SEO meta tags into `index.html` via `transformIndexHtml`. Resolves
20
+ * per-route SEO from `.fimo/config.json#seo` and the matched route.
21
+ *
22
+ * Only applies in projects that have an `index.html` (plain Vite). Conflicts
23
+ * with frameworks that have their own SEO mechanism — React Router's `meta`
24
+ * export, Astro's frontmatter, Next.js's `generateMetadata`. Use those
25
+ * instead via the framework adapter's SEO helpers.
26
+ *
27
+ * Opt-in via `fimo({ seo: true })` — only documented in the plain-Vite recipe.
28
+ */
29
+ seo?: boolean;
30
+ }
31
+ export declare function fimo(opts?: FimoViteOptions): PluginOption[];
32
+ //# sourceMappingURL=vite.d.ts.map
package/dist/vite.js ADDED
@@ -0,0 +1,98 @@
1
+ import runtimeEnvPlugin from 'fimo/vite/runtime-env';
2
+ import contentSyncPlugin from './plugins/content-sync.js';
3
+ import dataIdPlugin from './plugins/data-id.js';
4
+ import fimoConfigPlugin from './plugins/fimo-config.js';
5
+ import overlayPlugin from './plugins/overlay.js';
6
+ import robotsPlugin from './plugins/robots.js';
7
+ import seoPlugin from './plugins/seo.js';
8
+ import serverConfigPlugin from './plugins/server-config.js';
9
+ import sitemapPlugin from './plugins/sitemap.js';
10
+ import translationsPlugin from './plugins/translations.js';
11
+ // Force-bundle fimo into the SSR build, and let the dev optimizer pre-bundle
12
+ // it for the client. fimo's runtime imports `virtual:fimo-config` /
13
+ // `virtual:translations` / `virtual:fimo-overlay-*` — Vite-only specifiers
14
+ // resolved by sibling plugins — which esbuild can't see.
15
+ //
16
+ // Two places this matters:
17
+ // 1. SSR build: bundle fimo into dist/server (top-level `ssr.noExternal`
18
+ // + per-environment `configEnvironment` for react-router 7.15+, which
19
+ // doesn't merge viteUserConfig.environments.ssr for the default
20
+ // non-serverBundles case). Rollup resolves the virtuals through the
21
+ // plugin container, so SSR needs no special-casing.
22
+ // 2. Dev dep scan + optimization: esbuild chokes on `virtual:` ("Could not
23
+ // resolve 'virtual:fimo-config'"). The previous fix excluded the whole
24
+ // package from optimizeDeps, which served every runtime file as an
25
+ // individual module request (~34 extra round-trips per cold preview
26
+ // load through the sandbox proxy). Instead, externalize just the
27
+ // virtual specifiers — kept BARE (`virtual:fimo-config`, not an
28
+ // `/@id/`-prefixed URL): vite:import-analysis re-transforms optimized
29
+ // dep chunks and resolves bare specifiers through the plugin container,
30
+ // rewriting them to `/@id/` URLs itself. A pre-prefixed path fails that
31
+ // resolve ("Failed to resolve import '/@id/virtual:fimo-config'").
32
+ //
33
+ // Only `fimo` is named here. A framework package that also imports the
34
+ // virtual modules (fimo-react-router does) contributes its own package names
35
+ // through its own plugin; Vite concatenates the array config both return.
36
+ function bundleFimoIntoSsrPlugin() {
37
+ return {
38
+ name: 'vite-plugin-fimo-ssr-bundle',
39
+ config: () => ({
40
+ ssr: { noExternal: ['fimo'] },
41
+ optimizeDeps: {
42
+ rolldownOptions: {
43
+ plugins: [
44
+ {
45
+ name: 'fimo-externalize-virtuals',
46
+ resolveId(id) {
47
+ if (id.startsWith('virtual:')) {
48
+ return {
49
+ id,
50
+ external: true,
51
+ };
52
+ }
53
+ },
54
+ },
55
+ ],
56
+ },
57
+ },
58
+ }),
59
+ configEnvironment(name, config) {
60
+ if (name !== 'ssr')
61
+ return;
62
+ const existing = config.resolve?.noExternal;
63
+ const merged = existing === true
64
+ ? true
65
+ : Array.isArray(existing)
66
+ ? [...existing, 'fimo']
67
+ : existing
68
+ ? [existing, 'fimo']
69
+ : ['fimo'];
70
+ return {
71
+ resolve: { noExternal: merged },
72
+ };
73
+ },
74
+ };
75
+ }
76
+ // NOTE: dataIdPlugin must run before anything else to capture valid line numbers.
77
+ export function fimo(opts = {}) {
78
+ const plugins = [
79
+ runtimeEnvPlugin(),
80
+ bundleFimoIntoSsrPlugin(),
81
+ contentSyncPlugin(),
82
+ serverConfigPlugin(),
83
+ fimoConfigPlugin(),
84
+ dataIdPlugin(),
85
+ translationsPlugin(),
86
+ overlayPlugin(),
87
+ ];
88
+ if (opts.sitemap) {
89
+ plugins.push(sitemapPlugin());
90
+ }
91
+ if (opts.robots) {
92
+ plugins.push(robotsPlugin());
93
+ }
94
+ if (opts.seo) {
95
+ plugins.push(seoPlugin());
96
+ }
97
+ return plugins;
98
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "fimo-vite",
3
+ "version": "0.21.0-experimental.1",
4
+ "description": "Framework-neutral Fimo Vite plugin. Pairs with the fimo package at the same version.",
5
+ "files": [
6
+ "dist/",
7
+ "!dist/**/*.d.ts.map",
8
+ "README.md"
9
+ ],
10
+ "type": "module",
11
+ "sideEffects": false,
12
+ "exports": {
13
+ "./package.json": "./package.json",
14
+ ".": {
15
+ "types": "./dist/vite.d.ts",
16
+ "import": "./dist/vite.js"
17
+ }
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "scripts": {
23
+ "build": "tsc -b tsconfig.json",
24
+ "check:types": "tsc -b tsconfig.json --noEmit",
25
+ "clean": "rm -rf dist tsconfig.tsbuildinfo",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest"
28
+ },
29
+ "dependencies": {
30
+ "@rollup/pluginutils": "^5.1.0",
31
+ "oxc-parser": "^0.118.0"
32
+ },
33
+ "devDependencies": {
34
+ "@fimo/tsconfig": "0.21.0-experimental.1",
35
+ "@types/node": "^22.15.29",
36
+ "fimo": "0.21.0-experimental.1",
37
+ "typescript": "7.0.2",
38
+ "vite": "^8.0.13",
39
+ "vitest": "^4.1.6"
40
+ },
41
+ "peerDependencies": {
42
+ "fimo": ">=0.14.0",
43
+ "vite": "^7.0.0 || ^8.0.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=20.12.0"
47
+ }
48
+ }