fimo-sveltekit 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.
- package/README.md +39 -0
- package/components/Boolean.svelte +19 -0
- package/components/Date.svelte +33 -0
- package/components/DateTime.svelte +35 -0
- package/components/FimoProvider.svelte +35 -0
- package/components/Image.svelte +80 -0
- package/components/Json.svelte +18 -0
- package/components/Label.svelte +16 -0
- package/components/Number.svelte +21 -0
- package/components/RichText.svelte +19 -0
- package/components/Text.svelte +30 -0
- package/components/Video.svelte +37 -0
- package/components/index.d.ts +11 -0
- package/components/index.js +11 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +11 -0
- package/dist/data-id.d.ts +3 -0
- package/dist/data-id.js +171 -0
- package/dist/runtime/__fixtures__/public-env.d.ts +2 -0
- package/dist/runtime/__fixtures__/public-env.js +1 -0
- package/dist/runtime/content.d.ts +25 -0
- package/dist/runtime/content.js +33 -0
- package/dist/runtime/context-contract.d.ts +5 -0
- package/dist/runtime/context-contract.js +2 -0
- package/dist/runtime/context.d.ts +8 -0
- package/dist/runtime/context.js +25 -0
- package/dist/runtime/forms.d.ts +15 -0
- package/dist/runtime/forms.js +15 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/internal.d.ts +5 -0
- package/dist/runtime/internal.js +11 -0
- package/dist/runtime/labels.d.ts +20 -0
- package/dist/runtime/labels.js +13 -0
- package/dist/runtime/preview.d.ts +27 -0
- package/dist/runtime/preview.js +69 -0
- package/dist/runtime/runtime.d.ts +9 -0
- package/dist/runtime/runtime.js +26 -0
- package/dist/vite.d.ts +12 -0
- package/dist/vite.js +14 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# fimo-sveltekit
|
|
2
|
+
|
|
3
|
+
Fimo's SvelteKit runtime and hosting adapter. Install it next to `fimo` at the
|
|
4
|
+
same version:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install fimo fimo-sveltekit
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
// svelte.config.js
|
|
12
|
+
import { fimo } from 'fimo-sveltekit/config';
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
kit: { adapter: fimo() },
|
|
16
|
+
};
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
// vite.config.ts
|
|
21
|
+
import { sveltekit } from '@sveltejs/kit/vite';
|
|
22
|
+
import { fimo } from 'fimo-sveltekit/vite';
|
|
23
|
+
import { defineConfig } from 'vite';
|
|
24
|
+
|
|
25
|
+
export default defineConfig({ plugins: [fimo(), sveltekit()] });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
| Entry | What it owns |
|
|
29
|
+
| --------------------------- | --------------------------------------------------------------- |
|
|
30
|
+
| `fimo-sveltekit` | `createFimo`, `getLabels`, `useLabels`, `createFimoFormClient` |
|
|
31
|
+
| `fimo-sveltekit/components` | `FimoProvider` plus the native source-tracked Svelte primitives |
|
|
32
|
+
| `fimo-sveltekit/vite` | Branch env and dev-only `data-fimo-id` source tagging |
|
|
33
|
+
| `fimo-sveltekit/config` | The hosting adapter, behind Fimo's config seam |
|
|
34
|
+
|
|
35
|
+
Everything SvelteKit-specific lives here so a project on another framework never
|
|
36
|
+
resolves a SvelteKit or Svelte peer graph. `fimo` keeps the framework-neutral
|
|
37
|
+
building blocks this package is built on.
|
|
38
|
+
|
|
39
|
+
Docs: https://fimo.ai/docs/cli/frameworks/sveltekit
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
import type { FimoBoolean } from 'fimo-sveltekit';
|
|
5
|
+
import { getFimoSource } from 'fimo-sveltekit';
|
|
6
|
+
|
|
7
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
|
|
8
|
+
value: FimoBoolean | boolean | null | undefined;
|
|
9
|
+
as?: keyof SvelteHTMLElements;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
let { value, as = 'span', ...props }: Props = $props();
|
|
14
|
+
const source = $derived(getFimoSource(value));
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
{#if value != null}
|
|
18
|
+
<svelte:element this={as} {...props} data-fimo-source={source}>{String(value.valueOf())}</svelte:element>
|
|
19
|
+
{/if}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLTimeAttributes } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
import type { FimoString } from 'fimo-sveltekit';
|
|
5
|
+
import { getFimoSource } from 'fimo-sveltekit';
|
|
6
|
+
|
|
7
|
+
type Props = Omit<HTMLTimeAttributes, 'datetime' | 'children'> & {
|
|
8
|
+
value: FimoString | globalThis.Date | string | number | null | undefined;
|
|
9
|
+
locale?: string;
|
|
10
|
+
options?: Intl.DateTimeFormatOptions;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
let { value, locale, options, ...props }: Props = $props();
|
|
14
|
+
const source = $derived(getFimoSource(value));
|
|
15
|
+
const rendered = $derived.by(() => {
|
|
16
|
+
if (value == null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const raw = value instanceof String ? value.toString() : value;
|
|
20
|
+
const date = new globalThis.Date(raw as string | number | globalThis.Date);
|
|
21
|
+
if (Number.isNaN(date.getTime())) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
datetime: date.toISOString().split('T')[0],
|
|
26
|
+
text: date.toLocaleDateString(locale, options ?? { year: 'numeric', month: 'short', day: 'numeric' }),
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
{#if rendered}
|
|
32
|
+
<time {...props} datetime={rendered.datetime} data-fimo-source={source}>{rendered.text}</time>
|
|
33
|
+
{/if}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLTimeAttributes } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
import type { FimoString } from 'fimo-sveltekit';
|
|
5
|
+
import { getFimoSource } from 'fimo-sveltekit';
|
|
6
|
+
|
|
7
|
+
type Props = Omit<HTMLTimeAttributes, 'datetime' | 'children'> & {
|
|
8
|
+
value: FimoString | globalThis.Date | string | number | null | undefined;
|
|
9
|
+
locale?: string;
|
|
10
|
+
options?: Intl.DateTimeFormatOptions;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
let { value, locale, options, ...props }: Props = $props();
|
|
14
|
+
const source = $derived(getFimoSource(value));
|
|
15
|
+
const rendered = $derived.by(() => {
|
|
16
|
+
if (value == null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const raw = value instanceof String ? value.toString() : value;
|
|
20
|
+
const date = new globalThis.Date(raw as string | number | globalThis.Date);
|
|
21
|
+
if (Number.isNaN(date.getTime())) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
// The whole instant, not just the day: a `datetime` field exists to name
|
|
26
|
+
// a moment, and truncating it here would throw the time away.
|
|
27
|
+
datetime: date.toISOString(),
|
|
28
|
+
text: date.toLocaleString(locale, options ?? { dateStyle: 'medium', timeStyle: 'short' }),
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
{#if rendered}
|
|
34
|
+
<time {...props} datetime={rendered.datetime} data-fimo-source={source}>{rendered.text}</time>
|
|
35
|
+
{/if}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { afterNavigate, goto, invalidateAll } from '$app/navigation';
|
|
3
|
+
import { env } from '$env/dynamic/public';
|
|
4
|
+
import type { LabelsSnapshot } from 'fimo-sveltekit';
|
|
5
|
+
import {
|
|
6
|
+
connectPreview,
|
|
7
|
+
ensurePreviewScript,
|
|
8
|
+
LABELS_KEY,
|
|
9
|
+
sendPreviewLocation,
|
|
10
|
+
type LabelsSource,
|
|
11
|
+
} from 'fimo-sveltekit/internal';
|
|
12
|
+
import { onMount, setContext, type Snippet } from 'svelte';
|
|
13
|
+
|
|
14
|
+
let { labels, children }: { labels: LabelsSnapshot; children: Snippet } = $props();
|
|
15
|
+
let previewEnabled = false;
|
|
16
|
+
|
|
17
|
+
setContext<LabelsSource>(LABELS_KEY, () => labels);
|
|
18
|
+
|
|
19
|
+
onMount(() => {
|
|
20
|
+
previewEnabled = ensurePreviewScript(env.PUBLIC_FIMO_PREVIEW_SCRIPT_URL);
|
|
21
|
+
if (!previewEnabled) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return connectPreview({ goto, invalidateAll, locale: () => labels.locale });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterNavigate(() => {
|
|
29
|
+
if (previewEnabled) {
|
|
30
|
+
sendPreviewLocation(labels.locale);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
{@render children()}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLImgAttributes } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
import type { FimoMedia } from 'fimo-sveltekit';
|
|
5
|
+
import { getFimoSource } from 'fimo-sveltekit';
|
|
6
|
+
import {
|
|
7
|
+
mergeSvelteImageStyle,
|
|
8
|
+
resolveImagePresentation,
|
|
9
|
+
type ImageFit,
|
|
10
|
+
} from 'fimo-sveltekit/internal';
|
|
11
|
+
|
|
12
|
+
type Props = Omit<HTMLImgAttributes, 'src' | 'alt' | 'width' | 'height' | 'sizes' | 'srcset'> & {
|
|
13
|
+
value: FimoMedia | null | undefined;
|
|
14
|
+
alt?: string;
|
|
15
|
+
quality?: number;
|
|
16
|
+
fit?: ImageFit;
|
|
17
|
+
unoptimized?: boolean;
|
|
18
|
+
overrideSrc?: string;
|
|
19
|
+
width?: string | number;
|
|
20
|
+
height?: string | number;
|
|
21
|
+
sizes?: string;
|
|
22
|
+
srcset?: string;
|
|
23
|
+
srcSet?: string;
|
|
24
|
+
hostSuffixes?: readonly string[];
|
|
25
|
+
style?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
value,
|
|
30
|
+
alt,
|
|
31
|
+
quality,
|
|
32
|
+
fit,
|
|
33
|
+
unoptimized,
|
|
34
|
+
overrideSrc,
|
|
35
|
+
width,
|
|
36
|
+
height,
|
|
37
|
+
sizes,
|
|
38
|
+
srcset,
|
|
39
|
+
srcSet,
|
|
40
|
+
hostSuffixes,
|
|
41
|
+
style,
|
|
42
|
+
...props
|
|
43
|
+
}: Props = $props();
|
|
44
|
+
|
|
45
|
+
const image = $derived(
|
|
46
|
+
value
|
|
47
|
+
? resolveImagePresentation({
|
|
48
|
+
src: value.url,
|
|
49
|
+
alt: alt ?? value.alt,
|
|
50
|
+
mediaWidth: value.width,
|
|
51
|
+
mediaHeight: value.height,
|
|
52
|
+
quality,
|
|
53
|
+
fit,
|
|
54
|
+
unoptimized,
|
|
55
|
+
overrideSrc,
|
|
56
|
+
width,
|
|
57
|
+
height,
|
|
58
|
+
sizes,
|
|
59
|
+
srcSet: srcset ?? srcSet,
|
|
60
|
+
hostSuffixes,
|
|
61
|
+
})
|
|
62
|
+
: null,
|
|
63
|
+
);
|
|
64
|
+
const imageStyle = $derived(image ? mergeSvelteImageStyle(style, image.objectFit) : style);
|
|
65
|
+
const source = $derived(getFimoSource(value));
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
{#if image}
|
|
69
|
+
<img
|
|
70
|
+
{...props}
|
|
71
|
+
src={image.src}
|
|
72
|
+
alt={image.alt}
|
|
73
|
+
width={image.width}
|
|
74
|
+
height={image.height}
|
|
75
|
+
sizes={image.sizes}
|
|
76
|
+
srcset={image.srcSet}
|
|
77
|
+
style={imageStyle}
|
|
78
|
+
data-fimo-source={source}
|
|
79
|
+
/>
|
|
80
|
+
{/if}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
import { getFimoSource } from 'fimo-sveltekit';
|
|
5
|
+
|
|
6
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
|
|
7
|
+
value: unknown;
|
|
8
|
+
as?: keyof SvelteHTMLElements;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let { value, as = 'span', ...props }: Props = $props();
|
|
13
|
+
const source = $derived(getFimoSource(value));
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
{#if value != null}
|
|
17
|
+
<svelte:element this={as} {...props} data-fimo-source={source}>{JSON.stringify(value)}</svelte:element>
|
|
18
|
+
{/if}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useLabels } from 'fimo-sveltekit';
|
|
3
|
+
import type { ComponentProps } from 'svelte';
|
|
4
|
+
|
|
5
|
+
import Text from './Text.svelte';
|
|
6
|
+
|
|
7
|
+
type Props = Omit<ComponentProps<typeof Text>, 'value'> & {
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let { value, ...props }: Props = $props();
|
|
12
|
+
const labels = useLabels();
|
|
13
|
+
const label = $derived(labels.t(value));
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<Text {...props} value={label} />
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
// Content numbers stay plain JS numbers (there is no FimoNumber wrapper), so
|
|
5
|
+
// this primitive has no source to expose: it formats for display only and is
|
|
6
|
+
// not editable in the preview.
|
|
7
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
|
|
8
|
+
value: number | null | undefined;
|
|
9
|
+
as?: keyof SvelteHTMLElements;
|
|
10
|
+
locale?: string;
|
|
11
|
+
options?: Intl.NumberFormatOptions;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
let { value, as = 'span', locale, options, ...props }: Props = $props();
|
|
16
|
+
const rendered = $derived(value != null && !Number.isNaN(value) ? value.toLocaleString(locale, options) : null);
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
{#if rendered != null}
|
|
20
|
+
<svelte:element this={as} {...props}>{rendered}</svelte:element>
|
|
21
|
+
{/if}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
import type { FimoRichText } from 'fimo-sveltekit';
|
|
5
|
+
import { getFimoSource } from 'fimo-sveltekit';
|
|
6
|
+
import { renderRichTextHtml } from 'fimo-sveltekit/internal';
|
|
7
|
+
|
|
8
|
+
type Props = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
9
|
+
value: FimoRichText | null | undefined;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let { value, ...props }: Props = $props();
|
|
13
|
+
const source = $derived(getFimoSource(value));
|
|
14
|
+
const html = $derived(renderRichTextHtml(value?.content));
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
{#if value}
|
|
18
|
+
<div {...props} data-fimo-source={source} data-fimo-richtext="">{@html html}</div>
|
|
19
|
+
{/if}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes, SvelteHTMLElements } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
import type { FimoString } from 'fimo-sveltekit';
|
|
5
|
+
import { getFimoParts, getFimoSource } from 'fimo-sveltekit';
|
|
6
|
+
|
|
7
|
+
type Props = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
|
|
8
|
+
value: FimoString | string | null | undefined;
|
|
9
|
+
as?: keyof SvelteHTMLElements;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
let { value, as = 'span', ...props }: Props = $props();
|
|
14
|
+
const parts = $derived(getFimoParts(value));
|
|
15
|
+
const source = $derived(getFimoSource(value));
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<svelte:element this={as} {...props} data-fimo-source={source}>
|
|
19
|
+
{#if parts && parts.length > 1}
|
|
20
|
+
{#each parts as part}
|
|
21
|
+
{#if part.source}
|
|
22
|
+
<span data-fimo-source={part.source}>{part.text}</span>
|
|
23
|
+
{:else}
|
|
24
|
+
{part.text}
|
|
25
|
+
{/if}
|
|
26
|
+
{/each}
|
|
27
|
+
{:else}
|
|
28
|
+
{String(value ?? '')}
|
|
29
|
+
{/if}
|
|
30
|
+
</svelte:element>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLVideoAttributes } from 'svelte/elements';
|
|
3
|
+
|
|
4
|
+
import type { FimoMedia } from 'fimo-sveltekit';
|
|
5
|
+
import { getFimoSource } from 'fimo-sveltekit';
|
|
6
|
+
|
|
7
|
+
type Props = Omit<HTMLVideoAttributes, 'src' | 'width' | 'height' | 'children'> & {
|
|
8
|
+
value: FimoMedia | null | undefined;
|
|
9
|
+
overrideSrc?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
value,
|
|
14
|
+
overrideSrc,
|
|
15
|
+
autoplay = true,
|
|
16
|
+
loop = true,
|
|
17
|
+
muted = true,
|
|
18
|
+
playsinline = true,
|
|
19
|
+
...props
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
const source = $derived(getFimoSource(value));
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
{#if value}
|
|
25
|
+
<!-- svelte-ignore a11y_media_has_caption -->
|
|
26
|
+
<video
|
|
27
|
+
{...props}
|
|
28
|
+
src={overrideSrc ?? value.url}
|
|
29
|
+
width={value.width}
|
|
30
|
+
height={value.height}
|
|
31
|
+
{autoplay}
|
|
32
|
+
{loop}
|
|
33
|
+
{muted}
|
|
34
|
+
{playsinline}
|
|
35
|
+
data-fimo-source={source}
|
|
36
|
+
></video>
|
|
37
|
+
{/if}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as Boolean } from './Boolean.svelte';
|
|
2
|
+
export { default as Date } from './Date.svelte';
|
|
3
|
+
export { default as DateTime } from './DateTime.svelte';
|
|
4
|
+
export { default as FimoProvider } from './FimoProvider.svelte';
|
|
5
|
+
export { default as Image } from './Image.svelte';
|
|
6
|
+
export { default as Json } from './Json.svelte';
|
|
7
|
+
export { default as Label } from './Label.svelte';
|
|
8
|
+
export { default as Number } from './Number.svelte';
|
|
9
|
+
export { default as RichText } from './RichText.svelte';
|
|
10
|
+
export { default as Text } from './Text.svelte';
|
|
11
|
+
export { default as Video } from './Video.svelte';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as Boolean } from './Boolean.svelte';
|
|
2
|
+
export { default as Date } from './Date.svelte';
|
|
3
|
+
export { default as DateTime } from './DateTime.svelte';
|
|
4
|
+
export { default as FimoProvider } from './FimoProvider.svelte';
|
|
5
|
+
export { default as Image } from './Image.svelte';
|
|
6
|
+
export { default as Json } from './Json.svelte';
|
|
7
|
+
export { default as Label } from './Label.svelte';
|
|
8
|
+
export { default as Number } from './Number.svelte';
|
|
9
|
+
export { default as RichText } from './RichText.svelte';
|
|
10
|
+
export { default as Text } from './Text.svelte';
|
|
11
|
+
export { default as Video } from './Video.svelte';
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Adapter } from '@sveltejs/kit';
|
|
2
|
+
/**
|
|
3
|
+
* Configure a SvelteKit project for Fimo hosting without exposing the hosting
|
|
4
|
+
* provider in the generated application: `kit: { adapter: fimo() }`.
|
|
5
|
+
*
|
|
6
|
+
* Lives outside `fimo` so the provider adapter and its SvelteKit peer graph are
|
|
7
|
+
* only installed by SvelteKit projects (FIMO-1635).
|
|
8
|
+
*/
|
|
9
|
+
export declare function fimo(): Adapter;
|
|
10
|
+
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import adapter from '@sveltejs/adapter-vercel';
|
|
2
|
+
/**
|
|
3
|
+
* Configure a SvelteKit project for Fimo hosting without exposing the hosting
|
|
4
|
+
* provider in the generated application: `kit: { adapter: fimo() }`.
|
|
5
|
+
*
|
|
6
|
+
* Lives outside `fimo` so the provider adapter and its SvelteKit peer graph are
|
|
7
|
+
* only installed by SvelteKit projects (FIMO-1635).
|
|
8
|
+
*/
|
|
9
|
+
export function fimo() {
|
|
10
|
+
return adapter({ runtime: 'nodejs24.x' });
|
|
11
|
+
}
|
package/dist/data-id.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { getLineColumn } from 'fimo/vite/data-id';
|
|
5
|
+
// Native DOM elements are always taggable. Fimo's editable primitives are the
|
|
6
|
+
// only component exception: they forward arbitrary element attributes to the
|
|
7
|
+
// native element they render, so tagging them gives Studio the precise source
|
|
8
|
+
// location without leaking data-fimo-id props into arbitrary user components.
|
|
9
|
+
const TAGGABLE_ELEMENT_TYPES = new Set(['RegularElement', 'SvelteElement']);
|
|
10
|
+
const FIMO_PRIMITIVE_EXPORTS = new Set(['Text', 'Image', 'RichText']);
|
|
11
|
+
const FIMO_COMPONENTS_MODULE = 'fimo-sveltekit/components';
|
|
12
|
+
const EXCLUDED_ID_SEGMENTS = ['/node_modules/', '/.svelte-kit/', 'components/ui/', '.fimo/ui/'];
|
|
13
|
+
export default function svelteDataIdPlugin() {
|
|
14
|
+
let config;
|
|
15
|
+
let compilerLoad;
|
|
16
|
+
// Svelte is owned by the consuming app, never by fimo: resolve the compiler
|
|
17
|
+
// from the project root so tagging parses with the exact Svelte version
|
|
18
|
+
// that later compiles the file.
|
|
19
|
+
function loadCompiler() {
|
|
20
|
+
compilerLoad ??= (async () => {
|
|
21
|
+
try {
|
|
22
|
+
const projectRequire = createRequire(join(config.root, 'package.json'));
|
|
23
|
+
const compilerUrl = pathToFileURL(projectRequire.resolve('svelte/compiler')).href;
|
|
24
|
+
// The require condition can point at a CJS bundle; depending on the
|
|
25
|
+
// named-export lexer, `parse` may only be reachable via `default`.
|
|
26
|
+
const imported = (await import(compilerUrl));
|
|
27
|
+
const compiler = typeof imported.parse === 'function' ? imported : imported.default;
|
|
28
|
+
if (typeof compiler?.parse !== 'function') {
|
|
29
|
+
throw new Error('svelte/compiler does not expose parse()');
|
|
30
|
+
}
|
|
31
|
+
return compiler;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
config.logger.warn(`[fimo] data-fimo-id tagging disabled: svelte/compiler is not resolvable from ${config.root} (${String(error)})`);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
})();
|
|
38
|
+
return compilerLoad;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
name: 'vite-plugin-svelte-data-id',
|
|
42
|
+
enforce: 'pre',
|
|
43
|
+
configResolved(resolvedConfig) {
|
|
44
|
+
config = resolvedConfig;
|
|
45
|
+
},
|
|
46
|
+
async transform(code, id) {
|
|
47
|
+
if (config.command === 'build') {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
// Query'd sub-requests (`+page.svelte?svelte&type=style…`) carry
|
|
51
|
+
// compiled non-Svelte content and never end with `.svelte`.
|
|
52
|
+
if (!id.endsWith('.svelte')) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (EXCLUDED_ID_SEGMENTS.some((segment) => id.includes(segment))) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const compiler = await loadCompiler();
|
|
59
|
+
if (!compiler) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = compiler.parse(code, { modern: true });
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Invalid Svelte: leave the file untouched so vite-plugin-svelte
|
|
68
|
+
// reports the real syntax error against the original source.
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const relativePath = id.startsWith(`${config.root}/`) ? id.slice(config.root.length + 1) : id;
|
|
72
|
+
const insertions = [];
|
|
73
|
+
const primitiveComponents = collectFimoPrimitiveComponents(parsed.instance, parsed.module);
|
|
74
|
+
collectInsertions(parsed.fragment, code, relativePath, primitiveComponents, insertions);
|
|
75
|
+
if (insertions.length === 0) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
insertions.sort((a, b) => b.offset - a.offset);
|
|
79
|
+
let transformed = code;
|
|
80
|
+
for (const insertion of insertions) {
|
|
81
|
+
transformed = transformed.slice(0, insertion.offset) + insertion.value + transformed.slice(insertion.offset);
|
|
82
|
+
}
|
|
83
|
+
return { code: transformed, map: null };
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function collectInsertions(node, code, relativePath, primitiveComponents, insertions) {
|
|
88
|
+
if (!node || typeof node !== 'object') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(node)) {
|
|
92
|
+
for (const item of node) {
|
|
93
|
+
collectInsertions(item, code, relativePath, primitiveComponents, insertions);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const element = node;
|
|
98
|
+
if (typeof element.type === 'string' &&
|
|
99
|
+
(TAGGABLE_ELEMENT_TYPES.has(element.type) ||
|
|
100
|
+
(element.type === 'Component' && typeof element.name === 'string' && primitiveComponents.has(element.name))) &&
|
|
101
|
+
typeof element.start === 'number' &&
|
|
102
|
+
typeof element.name === 'string' &&
|
|
103
|
+
!hasExplicitId(element.attributes)) {
|
|
104
|
+
const { line, column } = getLineColumn(code, element.start);
|
|
105
|
+
insertions.push({
|
|
106
|
+
// `start` points at `<`, so this lands right after the tag name — also
|
|
107
|
+
// valid for `<svelte:element>`, whose `name` is the literal pseudo-tag.
|
|
108
|
+
offset: element.start + 1 + element.name.length,
|
|
109
|
+
value: ` data-fimo-id="${relativePath}#${line}-${column}"`,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
for (const [key, child] of Object.entries(node)) {
|
|
113
|
+
// `metadata` is compiler-internal bookkeeping and may hold
|
|
114
|
+
// back-references; `parent` would make the walk cyclic.
|
|
115
|
+
if (key === 'parent' || key === 'metadata') {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
collectInsertions(child, code, relativePath, primitiveComponents, insertions);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function collectFimoPrimitiveComponents(...instances) {
|
|
122
|
+
const names = new Set();
|
|
123
|
+
for (const instance of instances) {
|
|
124
|
+
if (!instance || typeof instance !== 'object') {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const content = instance.content;
|
|
128
|
+
if (!content || typeof content !== 'object') {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const body = content.body;
|
|
132
|
+
if (!Array.isArray(body)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
for (const statement of body) {
|
|
136
|
+
if (!statement || typeof statement !== 'object') {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const declaration = statement;
|
|
140
|
+
const source = declaration.source;
|
|
141
|
+
if (declaration.type !== 'ImportDeclaration' || source?.value !== FIMO_COMPONENTS_MODULE) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (!Array.isArray(declaration.specifiers)) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
for (const specifier of declaration.specifiers) {
|
|
148
|
+
if (!specifier || typeof specifier !== 'object') {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const imported = specifier;
|
|
152
|
+
if (imported.type === 'ImportSpecifier' &&
|
|
153
|
+
typeof imported.imported?.name === 'string' &&
|
|
154
|
+
FIMO_PRIMITIVE_EXPORTS.has(imported.imported.name) &&
|
|
155
|
+
typeof imported.local?.name === 'string') {
|
|
156
|
+
names.add(imported.local.name);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return names;
|
|
162
|
+
}
|
|
163
|
+
function hasExplicitId(attributes) {
|
|
164
|
+
if (!Array.isArray(attributes)) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return attributes.some((attribute) => {
|
|
168
|
+
const attr = attribute;
|
|
169
|
+
return attr.type === 'Attribute' && attr.name === 'data-fimo-id';
|
|
170
|
+
});
|
|
171
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const env = {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type CollectionContentCore, type ContentClientConfig, type ContentClientHandle, type SingletonContentCore } from 'fimo/content';
|
|
2
|
+
export interface SvelteKitContentClientConfig extends ContentClientConfig {
|
|
3
|
+
apiUrl: string | undefined;
|
|
4
|
+
defaultLocale?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SvelteKitEvent {
|
|
7
|
+
fetch: typeof globalThis.fetch;
|
|
8
|
+
}
|
|
9
|
+
export interface SvelteKitContentModule {
|
|
10
|
+
default: ContentClientHandle;
|
|
11
|
+
}
|
|
12
|
+
export type SvelteKitContentRegistry = Readonly<Record<string, SvelteKitContentModule>>;
|
|
13
|
+
export interface CreateFimoOptions<TContent extends SvelteKitContentRegistry> {
|
|
14
|
+
content: TContent;
|
|
15
|
+
event: SvelteKitEvent;
|
|
16
|
+
locale?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function createContentClient<T extends {
|
|
19
|
+
id: string;
|
|
20
|
+
}, TInput extends object>(config: SvelteKitContentClientConfig): CollectionContentCore<T, TInput>;
|
|
21
|
+
export declare function createSingletonClient<T extends {
|
|
22
|
+
id: string;
|
|
23
|
+
}, TInput extends object>(config: SvelteKitContentClientConfig): SingletonContentCore<T, TInput>;
|
|
24
|
+
export declare function createFimo<const TContent extends SvelteKitContentRegistry>({ content, event, locale, }: CreateFimoOptions<TContent>): TContent;
|
|
25
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createServerContentClient, createServerSingletonClient, } from 'fimo/content';
|
|
2
|
+
import { createSvelteKitContentRuntime } from './runtime.js';
|
|
3
|
+
const requestClientFactories = new WeakMap();
|
|
4
|
+
export function createContentClient(config) {
|
|
5
|
+
const create = (requestFetch, locale) => createServerContentClient(config, createSvelteKitContentRuntime({
|
|
6
|
+
apiUrl: config.apiUrl,
|
|
7
|
+
defaultLocale: locale ?? config.defaultLocale,
|
|
8
|
+
fetch: requestFetch,
|
|
9
|
+
}));
|
|
10
|
+
const client = create(globalThis.fetch);
|
|
11
|
+
requestClientFactories.set(client, create);
|
|
12
|
+
return client;
|
|
13
|
+
}
|
|
14
|
+
export function createSingletonClient(config) {
|
|
15
|
+
const create = (requestFetch, locale) => createServerSingletonClient(config, createSvelteKitContentRuntime({
|
|
16
|
+
apiUrl: config.apiUrl,
|
|
17
|
+
defaultLocale: locale ?? config.defaultLocale,
|
|
18
|
+
fetch: requestFetch,
|
|
19
|
+
}));
|
|
20
|
+
const client = create(globalThis.fetch);
|
|
21
|
+
requestClientFactories.set(client, create);
|
|
22
|
+
return client;
|
|
23
|
+
}
|
|
24
|
+
export function createFimo({ content, event, locale, }) {
|
|
25
|
+
return Object.fromEntries(Object.entries(content).map(([key, contentModule]) => {
|
|
26
|
+
const create = requestClientFactories.get(contentModule.default);
|
|
27
|
+
if (!create) {
|
|
28
|
+
throw new Error(`Content module "${key}" was not created by fimo-sveltekit.`);
|
|
29
|
+
}
|
|
30
|
+
const client = create(event.fetch, locale);
|
|
31
|
+
return [key, { ...contentModule, ...client, default: client }];
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LabelsSnapshot } from 'fimo/labels';
|
|
2
|
+
/** Shared by duplicated `fimo-sveltekit` instances in the same Svelte tree. */
|
|
3
|
+
export declare const LABELS_KEY: unique symbol;
|
|
4
|
+
export type LabelsSource = () => LabelsSnapshot;
|
|
5
|
+
//# sourceMappingURL=context-contract.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Labels } from 'fimo/labels';
|
|
2
|
+
/**
|
|
3
|
+
* Read the labels installed by `FimoProvider`. Call it while a component
|
|
4
|
+
* initializes; the returned `t()` is usable at any time afterwards and returns
|
|
5
|
+
* a source-tracked `FimoString`, empty for keys the project has no value for.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useLabels(): Labels;
|
|
8
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createLabels } from 'fimo/labels';
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import { LABELS_KEY } from './context-contract.js';
|
|
4
|
+
/**
|
|
5
|
+
* Read the labels installed by `FimoProvider`. Call it while a component
|
|
6
|
+
* initializes; the returned `t()` is usable at any time afterwards and returns
|
|
7
|
+
* a source-tracked `FimoString`, empty for keys the project has no value for.
|
|
8
|
+
*/
|
|
9
|
+
export function useLabels() {
|
|
10
|
+
const readSnapshot = getContext(LABELS_KEY);
|
|
11
|
+
if (!readSnapshot) {
|
|
12
|
+
throw new Error('useLabels() found no Fimo labels. Render FimoProvider with the label snapshot from your root layout load.');
|
|
13
|
+
}
|
|
14
|
+
// Every member reads the snapshot on access: binding one `createLabels` result
|
|
15
|
+
// here would freeze the labels at initialization and stop tracking `data`.
|
|
16
|
+
return {
|
|
17
|
+
get locale() {
|
|
18
|
+
return readSnapshot().locale;
|
|
19
|
+
},
|
|
20
|
+
get snapshot() {
|
|
21
|
+
return readSnapshot();
|
|
22
|
+
},
|
|
23
|
+
t: (key) => createLabels(readSnapshot()).t(key),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type FormClient } from 'fimo/forms';
|
|
2
|
+
import type { SvelteKitEvent } from './content.js';
|
|
3
|
+
export interface CreateFimoFormClientOptions {
|
|
4
|
+
event?: SvelteKitEvent;
|
|
5
|
+
apiUrl?: string;
|
|
6
|
+
defaultLocale?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Create a form client for a SvelteKit request. Pass `event` from a server
|
|
10
|
+
* action or load function when the request-aware fetch should be preserved.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createFimoFormClient({ event, apiUrl, defaultLocale }?: CreateFimoFormClientOptions): FormClient;
|
|
13
|
+
/** Browser-friendly default for simple client-side submissions. */
|
|
14
|
+
export declare const formClient: FormClient;
|
|
15
|
+
//# sourceMappingURL=forms.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createFormClient } from 'fimo/forms';
|
|
2
|
+
import { createSvelteKitContentRuntime } from './runtime.js';
|
|
3
|
+
/**
|
|
4
|
+
* Create a form client for a SvelteKit request. Pass `event` from a server
|
|
5
|
+
* action or load function when the request-aware fetch should be preserved.
|
|
6
|
+
*/
|
|
7
|
+
export function createFimoFormClient({ event, apiUrl, defaultLocale } = {}) {
|
|
8
|
+
return createFormClient(createSvelteKitContentRuntime({
|
|
9
|
+
apiUrl,
|
|
10
|
+
defaultLocale,
|
|
11
|
+
fetch: event?.fetch ?? globalThis.fetch,
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
/** Browser-friendly default for simple client-side submissions. */
|
|
15
|
+
export const formClient = createFimoFormClient();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { createContentClient, createFimo, createSingletonClient, type CreateFimoOptions, type SvelteKitContentClientConfig, type SvelteKitContentModule, type SvelteKitContentRegistry, type SvelteKitEvent, } from './content.js';
|
|
2
|
+
export { useLabels } from './context.js';
|
|
3
|
+
export { getLabels, type GetLabelsOptions } from './labels.js';
|
|
4
|
+
export { createFimoFormClient, formClient } from './forms.js';
|
|
5
|
+
export { type FormClient, type FormSubmissionResult } from 'fimo/forms';
|
|
6
|
+
export type { Labels, LabelsSnapshot } from 'fimo/labels';
|
|
7
|
+
export type { CollectionContentCore as CollectionContentClient, SingletonContentCore as SingletonContentClient, } from 'fimo/content';
|
|
8
|
+
export * from 'fimo/content';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { renderRichTextHtml, resolveImagePresentation, type ImageFit } from 'fimo/primitives';
|
|
2
|
+
export { LABELS_KEY, type LabelsSource } from './context-contract.js';
|
|
3
|
+
export { connectPreview, ensurePreviewScript, sendPreviewLocation, type PreviewNavigation } from './preview.js';
|
|
4
|
+
export declare function mergeSvelteImageStyle(style: string | null | undefined, objectFit: string): string;
|
|
5
|
+
//# sourceMappingURL=internal.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { renderRichTextHtml, resolveImagePresentation } from 'fimo/primitives';
|
|
2
|
+
export { LABELS_KEY } from './context-contract.js';
|
|
3
|
+
export { connectPreview, ensurePreviewScript, sendPreviewLocation } from './preview.js';
|
|
4
|
+
export function mergeSvelteImageStyle(style, objectFit) {
|
|
5
|
+
if (style && /(?:^|;)\s*object-fit\s*:/i.test(style)) {
|
|
6
|
+
return style;
|
|
7
|
+
}
|
|
8
|
+
const value = style?.trim() ?? '';
|
|
9
|
+
const separator = value === '' || value.endsWith(';') ? '' : ';';
|
|
10
|
+
return `${value}${separator}object-fit:${objectFit}`;
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type LabelsSnapshot } from 'fimo/labels';
|
|
2
|
+
import type { SvelteKitEvent } from './content.js';
|
|
3
|
+
export interface GetLabelsOptions {
|
|
4
|
+
/** Optional escape hatch for a custom content API endpoint. */
|
|
5
|
+
apiUrl?: string;
|
|
6
|
+
event: SvelteKitEvent;
|
|
7
|
+
/**
|
|
8
|
+
* The application resolves the active locale; Fimo never infers it from the
|
|
9
|
+
* URL. Omit it to let the Fimo API answer with the project default.
|
|
10
|
+
*/
|
|
11
|
+
locale?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Read the label dictionary for one locale through the request `fetch`.
|
|
15
|
+
*
|
|
16
|
+
* Returns plain data because SvelteKit serializes whatever a `load` returns:
|
|
17
|
+
* pass it to `FimoProvider` in a layout to expose `Label` and `useLabels()`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getLabels({ apiUrl, event, locale }: GetLabelsOptions): Promise<LabelsSnapshot>;
|
|
20
|
+
//# sourceMappingURL=labels.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { fetchLabels } from 'fimo/labels';
|
|
2
|
+
import { createSvelteKitContentRuntime } from './runtime.js';
|
|
3
|
+
/**
|
|
4
|
+
* Read the label dictionary for one locale through the request `fetch`.
|
|
5
|
+
*
|
|
6
|
+
* Returns plain data because SvelteKit serializes whatever a `load` returns:
|
|
7
|
+
* pass it to `FimoProvider` in a layout to expose `Label` and `useLabels()`.
|
|
8
|
+
*/
|
|
9
|
+
export async function getLabels({ apiUrl, event, locale }) {
|
|
10
|
+
const runtime = createSvelteKitContentRuntime({ apiUrl, fetch: event.fetch });
|
|
11
|
+
const labels = await fetchLabels(runtime, { locale });
|
|
12
|
+
return labels.snapshot;
|
|
13
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export { ensurePreviewScript } from 'fimo/preview';
|
|
2
|
+
export interface PreviewNavigation {
|
|
3
|
+
/** SvelteKit `goto` — the app router stays in charge of the transition. */
|
|
4
|
+
goto: (to: string) => unknown;
|
|
5
|
+
/** SvelteKit `invalidateAll` — reruns every `load` against fresh content. */
|
|
6
|
+
invalidateAll: () => Promise<unknown>;
|
|
7
|
+
/** Optional app-owned locale resolver, evaluated for every bridge message. */
|
|
8
|
+
locale?: () => string | undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Report where the app is now, and in which locale. Studio keeps its URL,
|
|
12
|
+
* route selector and content locale in sync from this.
|
|
13
|
+
*
|
|
14
|
+
* `locale` defaults to the locale the SvelteKit shell rendered into
|
|
15
|
+
* `<html lang>`; pass one explicitly when the app resolves it somewhere the
|
|
16
|
+
* document does not show, for example a cookie or a user preference.
|
|
17
|
+
*/
|
|
18
|
+
export declare function sendPreviewLocation(locale?: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Listen for the live Studio preview protocol and answer it with SvelteKit's
|
|
21
|
+
* own navigation APIs. Returns the disconnect function.
|
|
22
|
+
*
|
|
23
|
+
* Only messages from the established parent origin are honored. History stays
|
|
24
|
+
* native, so Studio's back/forward controls need no protocol of their own.
|
|
25
|
+
*/
|
|
26
|
+
export declare function connectPreview({ goto, invalidateAll, locale }: PreviewNavigation): () => void;
|
|
27
|
+
//# sourceMappingURL=preview.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { getDocumentLocale, getPreviewLocationChange, getPreviewParentOrigin } from 'fimo/preview';
|
|
2
|
+
export { ensurePreviewScript } from 'fimo/preview';
|
|
3
|
+
/**
|
|
4
|
+
* Report where the app is now, and in which locale. Studio keeps its URL,
|
|
5
|
+
* route selector and content locale in sync from this.
|
|
6
|
+
*
|
|
7
|
+
* `locale` defaults to the locale the SvelteKit shell rendered into
|
|
8
|
+
* `<html lang>`; pass one explicitly when the app resolves it somewhere the
|
|
9
|
+
* document does not show, for example a cookie or a user preference.
|
|
10
|
+
*/
|
|
11
|
+
export function sendPreviewLocation(locale) {
|
|
12
|
+
const parentOrigin = getPreviewParentOrigin();
|
|
13
|
+
window.parent.postMessage({
|
|
14
|
+
type: 'nav/locationChange',
|
|
15
|
+
payload: getPreviewLocationChange(window.location, locale ?? getDocumentLocale(document, 'en'), parentOrigin),
|
|
16
|
+
}, parentOrigin ?? '*');
|
|
17
|
+
}
|
|
18
|
+
/** Reliable-delivery reply: the parent retries an unacknowledged message forever. */
|
|
19
|
+
function acknowledge(message, result) {
|
|
20
|
+
const parentOrigin = getPreviewParentOrigin();
|
|
21
|
+
if (!message.requiresAck || typeof message.messageId !== 'string' || !parentOrigin) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
window.parent.postMessage({ type: 'ack', payload: { messageId: message.messageId, ...result } }, parentOrigin);
|
|
25
|
+
}
|
|
26
|
+
async function refreshContent(message, invalidateAll) {
|
|
27
|
+
try {
|
|
28
|
+
await invalidateAll();
|
|
29
|
+
acknowledge(message, { ok: true });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error(error);
|
|
33
|
+
acknowledge(message, { ok: false, error: error instanceof Error ? error.message : String(error) });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Listen for the live Studio preview protocol and answer it with SvelteKit's
|
|
38
|
+
* own navigation APIs. Returns the disconnect function.
|
|
39
|
+
*
|
|
40
|
+
* Only messages from the established parent origin are honored. History stays
|
|
41
|
+
* native, so Studio's back/forward controls need no protocol of their own.
|
|
42
|
+
*/
|
|
43
|
+
export function connectPreview({ goto, invalidateAll, locale }) {
|
|
44
|
+
const onMessage = (event) => {
|
|
45
|
+
const parentOrigin = getPreviewParentOrigin();
|
|
46
|
+
if (event.source !== window.parent || !parentOrigin || event.origin !== parentOrigin) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const message = (event.data ?? {});
|
|
50
|
+
switch (message.type) {
|
|
51
|
+
case 'nav/navigate':
|
|
52
|
+
if (typeof message.payload?.to === 'string') {
|
|
53
|
+
void goto(message.payload.to);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
case 'nav/read-routes':
|
|
57
|
+
// The app owns its route table; answer with where it actually is.
|
|
58
|
+
sendPreviewLocation(locale?.());
|
|
59
|
+
return;
|
|
60
|
+
case 'content/refresh':
|
|
61
|
+
void refreshContent(message, invalidateAll);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
window.addEventListener('message', onMessage);
|
|
66
|
+
return () => {
|
|
67
|
+
window.removeEventListener('message', onMessage);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ContentRuntime } from 'fimo/content';
|
|
2
|
+
export interface SvelteKitContentRuntimeOptions {
|
|
3
|
+
/** Optional escape hatch for tests or deliberately custom API endpoints. */
|
|
4
|
+
apiUrl?: string;
|
|
5
|
+
defaultLocale?: string;
|
|
6
|
+
fetch: typeof globalThis.fetch;
|
|
7
|
+
}
|
|
8
|
+
export declare function createSvelteKitContentRuntime(options: SvelteKitContentRuntimeOptions): ContentRuntime;
|
|
9
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { env as publicEnv } from '$env/dynamic/public';
|
|
2
|
+
import { requireContentApiUrl } from 'fimo/content';
|
|
3
|
+
function resolveApiUrl(apiUrl) {
|
|
4
|
+
return (apiUrl ??
|
|
5
|
+
publicEnv.PUBLIC_FIMO_API_URL ??
|
|
6
|
+
(typeof process === 'undefined' ? undefined : process.env.PUBLIC_FIMO_API_URL));
|
|
7
|
+
}
|
|
8
|
+
export function createSvelteKitContentRuntime(options) {
|
|
9
|
+
const apiUrl = resolveApiUrl(options.apiUrl);
|
|
10
|
+
return {
|
|
11
|
+
apiBase() {
|
|
12
|
+
return requireContentApiUrl(apiUrl, 'PUBLIC_FIMO_API_URL');
|
|
13
|
+
},
|
|
14
|
+
defaultLocale() {
|
|
15
|
+
// `undefined` leaves `locale` off the request so the Fimo API answers with
|
|
16
|
+
// the project default; hardcoding `en` here would silently override it.
|
|
17
|
+
return options.defaultLocale?.trim() || undefined;
|
|
18
|
+
},
|
|
19
|
+
headers(extra) {
|
|
20
|
+
return { ...extra };
|
|
21
|
+
},
|
|
22
|
+
fetch(input, init) {
|
|
23
|
+
return options.fetch(input, init);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PluginOption } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* Narrow SvelteKit build connector. Points dev and production builds at the
|
|
4
|
+
* git branch's Fimo env (`PUBLIC_FIMO_API_URL`, `FIMO_ENV`), and tags native Svelte
|
|
5
|
+
* template elements with deterministic `data-fimo-id="<path>#<line>-<column>"`
|
|
6
|
+
* attributes during `vite dev` so the Fimo preview can map DOM nodes back to
|
|
7
|
+
* source. Explicit `data-fimo-id` attributes are preserved.
|
|
8
|
+
*
|
|
9
|
+
* Usage in the app's vite.config.ts: `plugins: [fimo(), sveltekit()]`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function fimo(): PluginOption[];
|
|
12
|
+
//# sourceMappingURL=vite.d.ts.map
|
package/dist/vite.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import runtimeEnvPlugin from 'fimo/vite/runtime-env';
|
|
2
|
+
import svelteDataIdPlugin from './data-id.js';
|
|
3
|
+
/**
|
|
4
|
+
* Narrow SvelteKit build connector. Points dev and production builds at the
|
|
5
|
+
* git branch's Fimo env (`PUBLIC_FIMO_API_URL`, `FIMO_ENV`), and tags native Svelte
|
|
6
|
+
* template elements with deterministic `data-fimo-id="<path>#<line>-<column>"`
|
|
7
|
+
* attributes during `vite dev` so the Fimo preview can map DOM nodes back to
|
|
8
|
+
* source. Explicit `data-fimo-id` attributes are preserved.
|
|
9
|
+
*
|
|
10
|
+
* Usage in the app's vite.config.ts: `plugins: [fimo(), sveltekit()]`.
|
|
11
|
+
*/
|
|
12
|
+
export function fimo() {
|
|
13
|
+
return [runtimeEnvPlugin(), svelteDataIdPlugin()];
|
|
14
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fimo-sveltekit",
|
|
3
|
+
"version": "0.21.0-experimental.1",
|
|
4
|
+
"description": "Fimo runtime and hosting adapter for SvelteKit projects. Pairs with the fimo package at the same version.",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist/",
|
|
7
|
+
"!dist/**/*.d.ts.map",
|
|
8
|
+
"components/",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"exports": {
|
|
14
|
+
"./package.json": "./package.json",
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/runtime/index.d.ts",
|
|
17
|
+
"import": "./dist/runtime/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./components": {
|
|
20
|
+
"types": "./components/index.d.ts",
|
|
21
|
+
"svelte": "./components/index.js",
|
|
22
|
+
"import": "./components/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./internal": {
|
|
25
|
+
"types": "./dist/runtime/internal.d.ts",
|
|
26
|
+
"import": "./dist/runtime/internal.js"
|
|
27
|
+
},
|
|
28
|
+
"./vite": {
|
|
29
|
+
"types": "./dist/vite.d.ts",
|
|
30
|
+
"import": "./dist/vite.js"
|
|
31
|
+
},
|
|
32
|
+
"./config": {
|
|
33
|
+
"types": "./dist/config.d.ts",
|
|
34
|
+
"import": "./dist/config.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -b tsconfig.json",
|
|
42
|
+
"check:types": "tsc -b tsconfig.json --noEmit",
|
|
43
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"test:watch": "vitest"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@sveltejs/adapter-vercel": "6.3.4"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@fimo/tsconfig": "0.21.0-experimental.1",
|
|
52
|
+
"@types/node": "^22.15.29",
|
|
53
|
+
"fimo": "0.21.0-experimental.1",
|
|
54
|
+
"typescript": "7.0.2",
|
|
55
|
+
"vitest": "^4.1.6"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@sveltejs/kit": "^2.0.0",
|
|
59
|
+
"fimo": ">=0.14.0",
|
|
60
|
+
"svelte": "^5.0.0",
|
|
61
|
+
"vite": "^7.0.0 || ^8.0.0"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=20.12.0"
|
|
65
|
+
}
|
|
66
|
+
}
|