Skip to content

Commit

Permalink
Svelte 5 TS Adapter + Svelte 5 playground (Up to date)
Browse files Browse the repository at this point in the history
This is just a backup as I am currently reworking the main branch and Inertia is not planning to have a separate Svelte 5 adapter.

Note: There is currently a bug with NPM where packages are not properly scoped so you will get ParseError: Unexpected character '@' as an error. Using pnpm works as expected.

It is possible that with all other workspace packages up to date in this branch it resolves itself.
  • Loading branch information
jamesst20 committed May 16, 2024
1 parent ee760ea commit 0c12810
Show file tree
Hide file tree
Showing 89 changed files with 11,623 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:

strategy:
matrix:
adapter: ['react', 'vue2', 'vue3', 'svelte']
adapter: ['react', 'vue2', 'vue3', 'svelte', 'svelte5']
node-version: [20]

steps:
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export { default as createInertiaApp } from './createInertiaApp'
export { default as inertia } from './link'
export { default as page } from './page'
export { default as remember } from './remember'
export { default as useForm, InertiaForm } from './useForm'
export { default as useForm, type InertiaForm } from './useForm'
11 changes: 11 additions & 0 deletions packages/svelte5/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.DS_Store
node_modules
/build
/dist
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
21 changes: 21 additions & 0 deletions packages/svelte5/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) Jonathan Reinink <jonathan@reinink.ca>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
69 changes: 69 additions & 0 deletions packages/svelte5/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"name": "@inertiajs/svelte5",
"version": "1.0.16",
"license": "MIT",
"description": "The Svelte 5 adapter for Inertia.js",
"contributors": [
"Jonathan Reinink <jonathan@reinink.ca>",
"Pedro Borges <oi@pedroborg.es>"
],
"homepage": "https://inertiajs.com/",
"repository": {
"type": "git",
"url": "https://github.com/inertiajs/inertia.git",
"directory": "packages/svelte"
},
"bugs": {
"url": "https://github.com/inertiajs/inertia/issues"
},
"keywords": [
"svelte"
],
"scripts": {
"build": "npm run package",
"package": "svelte-kit sync && svelte-package && publint",
"prepublishOnly": "npm run package",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test": "vitest"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
},
"./server": {
"types": "./dist/server.d.ts",
"svelte": "./dist/server.js"
}
},
"files": [
"dist",
"!dist/**/*.test.*",
"!dist/**/*.spec.*"
],
"peerDependencies": {
"svelte": "^5.0.0-next.1"
},
"dependencies": {
"@inertiajs/core": "workspace:*",
"lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.5.0"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/package": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.0",
"publint": "^0.1.9",
"svelte": "^5.0.0-next.1",
"svelte-check": "^3.6.0",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^5.0.11",
"vitest": "^1.2.0"
},
"svelte": "./dist/index.js",
"types": "./dist/index.d.ts",
"type": "module"
}
11 changes: 11 additions & 0 deletions packages/svelte5/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Inertia.js Svelte 5 Adapter

Visit [inertiajs.com](https://inertiajs.com/) to learn more.

## Installation

```
npm install @jamesst20/inertia-svelte5
yarn add @jamesst20/inertia-svelte5
pnpm add @jamesst20/inertia-svelte5
```
19 changes: 19 additions & 0 deletions packages/svelte5/src/lib/components/App.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
import Render, { h } from './Render.svelte'
import store from '../store.svelte'
let child = $derived(store.component?.default && h(store.component.default, store.page?.props))
let layout = $derived(store.component && store.component.layout)
let components = $derived(
layout
? Array.isArray(layout)
? layout
.concat(child)
.reverse()
.reduce((child, layout) => h(layout, store.page?.props, [child]))
: h(layout, store.page?.props, child ? [child] : [])
: child,
)
</script>
<Render {...components} />
66 changes: 66 additions & 0 deletions packages/svelte5/src/lib/components/Link.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script context="module" lang="ts">
import type { Snippet } from 'svelte'
import type { Method, PreserveStateOption, RequestPayload } from '@inertiajs/core'
type LinkProps = {
href: string
as?: keyof HTMLElementTagNameMap
data?: RequestPayload
method?: Method
replace?: boolean
preserveScroll?: PreserveStateOption
preserveState?: PreserveStateOption | null
only?: string[]
headers?: Record<string, string>
queryStringArrayFormat?: 'brackets' | 'indices'
class?: string
children: Snippet
}
</script>
<script lang="ts">
import { default as inertia } from '../link'
let {
href,
as = 'a',
data = {},
method = 'get',
replace = false,
preserveScroll = false,
preserveState = null,
only = [],
headers = {},
queryStringArrayFormat = 'brackets',
children,
...restProps
}: LinkProps = $props()
$effect.pre(() => {
if (as === 'a' && method.toLowerCase() !== 'get') {
console.warn(
`Creating POST/PUT/PATCH/DELETE <a> links is discouraged as it causes "Open Link in New Tab/Window" accessibility issues.\n\nPlease specify a more appropriate element using the "as" attribute. For example:\n\n<Link href="${href}" method="${method}" as="button">...</Link>`,
)
}
})
</script>

<!-- svelte-ignore a11y-no-static-element-interactions -->
<svelte:element
this={as}
use:inertia={{
...(as !== 'a' ? { href } : {}),
data,
method,
replace,
preserveScroll,
preserveState: preserveState ?? method !== 'get',
only,
headers,
queryStringArrayFormat,
}}
{...as === 'a' ? { href } : {}}
{...restProps}
>
{@render children()}
</svelte:element>
44 changes: 44 additions & 0 deletions packages/svelte5/src/lib/components/Render.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script context="module" lang="ts">
import type { PageProps } from '@inertiajs/core'
import type { ComponentType } from 'svelte'
type RenderProps = {
component: ComponentType
props?: PageProps
childComponents?: RenderProps[]
}
export const h = (component: ComponentType, props?: PageProps, childComponents?: RenderProps[]): RenderProps => {
return {
component,
...(props ? { props } : {}),
...(childComponents ? { childComponents } : {}),
}
}
</script>

<script lang="ts">
import store from '../store.svelte.js'
let { component, props = {}, childComponents = [] }: RenderProps = $props()
let prevComponent: ComponentType
let key: number | null = $state(null)
$effect(() => {
if (prevComponent !== component) {
key = Date.now()
prevComponent = component
}
})
</script>

{#if store.component}
{#key key}
<svelte:component this={component} {...props}>
{#each childComponents as child, index (component && component.length === index ? store.key : null)}
<svelte:self {...child} />
{/each}
</svelte:component>
{/key}
{/if}
15 changes: 15 additions & 0 deletions packages/svelte5/src/lib/components/SSR.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script context="module" lang="ts">
import type { Page } from '@inertiajs/core'
export type SSRProps = { id: string; initialPage: Page }
</script>
<script lang="ts">
import App from './App.svelte'
let { id, initialPage }: SSRProps = $props()
</script>

<div data-server-rendered="true" {id} data-page={JSON.stringify(initialPage)}>
<App />
</div>
85 changes: 85 additions & 0 deletions packages/svelte5/src/lib/createInertiaApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { router, setupProgress, type InertiaAppResponse, type Page } from '@inertiajs/core'
import type { ComponentType } from 'svelte'
import App from './components/App.svelte'
import SSR from './components/SSR.svelte'
import store from './store.svelte'
import type { ComponentsResolver, ResolvedComponents } from './types'
import { render } from 'svelte/server'

interface CreateInertiaAppProps {
id?: string
resolve: ComponentsResolver
setup: (props: {
el: Element
App: ComponentType<App>
props: {
initialPage: Page
resolveComponent: ComponentsResolver
}
}) => void | App
progress?:
| false
| {
delay?: number
color?: string
includeCSS?: boolean
showSpinner?: boolean
}
page?: Page
}

export default async function createInertiaApp({
id = 'app',
resolve,
setup,
progress = {},
page,
}: CreateInertiaAppProps): InertiaAppResponse {
const isServer = typeof window === 'undefined'
const el = isServer ? null : document.getElementById(id)
const initialPage: Page = page || JSON.parse(el?.dataset?.page || '{}')
const resolveComponent = (name: string, page: Page) => Promise.resolve(resolve(name, page))

await resolveComponent(initialPage.component, initialPage).then((initialComponent) => {
store.component = initialComponent
store.page = initialPage
})

if (!isServer) {
if (!el) {
throw new Error(`Element with ID "${id}" not found.`)
}

router.init({
initialPage,
resolveComponent,
swapComponent: async ({ component, page, preserveState }) => {
store.component = component as ResolvedComponents
store.page = page
store.key = preserveState ? store.key : Date.now()
},
})

if (progress) {
setupProgress(progress)
}

setup({
el,
App,
props: {
initialPage,
resolveComponent,
},
})
}

if (isServer) {
const { html, head } = render(SSR as any, { props: { id, initialPage }})

return {
body: html,
head: [head],
}
}
}
7 changes: 7 additions & 0 deletions packages/svelte5/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { router } from '@inertiajs/core'
export { default as Link } from './components/Link.svelte'
export { default as createInertiaApp } from './createInertiaApp'
export { default as inertia } from './link'
export { default as page } from './page.svelte'
export { default as remember } from './remember.svelte'
export { default as useForm, type InertiaForm } from './useForm.svelte'
Loading

0 comments on commit 0c12810

Please sign in to comment.