Skip to content

Commit

Permalink
dev ssr
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmajor committed Oct 11, 2024
1 parent 3701734 commit 76bf077
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 166 deletions.
1 change: 1 addition & 0 deletions packages/core/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const builds = [
{ entryPoints: ['src/index.ts'], format: 'cjs', outfile: 'dist/index.js', platform: 'browser' },
{ entryPoints: ['src/server.ts'], format: 'esm', outfile: 'dist/server.esm.js', platform: 'node' },
{ entryPoints: ['src/server.ts'], format: 'cjs', outfile: 'dist/server.js', platform: 'node' },
{ entryPoints: ['src/vite.ts'], format: 'esm', outfile: 'dist/vite.js', platform: 'node' },
]

builds.forEach((build) => {
Expand Down
4 changes: 4 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
"types": "./types/server.d.ts",
"import": "./dist/server.esm.js",
"require": "./dist/server.js"
},
"./vite": {
"types": "./types/vite.d.ts",
"default": "./dist/vite.js"
}
},
"typesVersions": {
Expand Down
14 changes: 4 additions & 10 deletions packages/core/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import { createServer, IncomingMessage } from 'http'
import * as process from 'process'
import { InertiaAppResponse, Page } from './types'
import { readableToString } from './serverUtils'
import type { InertiaAppResponse, Page } from './types'

type AppCallback = (page: Page) => InertiaAppResponse
type RouteHandler = (request: IncomingMessage) => Promise<unknown>
export type AppCallback = (page: Page) => InertiaAppResponse

const readableToString: (readable: IncomingMessage) => Promise<string> = (readable) =>
new Promise((resolve, reject) => {
let data = ''
readable.on('data', (chunk) => (data += chunk))
readable.on('end', () => resolve(data))
readable.on('error', (err) => reject(err))
})
type RouteHandler = (request: IncomingMessage) => Promise<unknown>

export default (render: AppCallback, port?: number): void => {
const _port = port || 13714
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/serverUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { IncomingMessage } from 'http'

export const readableToString: (readable: IncomingMessage) => Promise<string> = (readable) =>
new Promise((resolve, reject) => {
let data = ''
readable.on('data', (chunk) => (data += chunk))
readable.on('end', () => resolve(data))
readable.on('error', (err) => reject(err))
})
28 changes: 28 additions & 0 deletions packages/core/src/vite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Plugin } from 'vite'
import { readableToString } from './serverUtils'

interface PluginConfig {
renderer: string
}

export default function inertia(config: PluginConfig): Plugin {
// todo: validate config

return {
name: '@inertiajs/svelte/ssr',
async configureServer(server) {
return () =>
server.middlewares.use(async (req, res, next) => {
if (req.url !== '/render') {
next()
}

// todo: check if render is a function
const { render } = await server.ssrLoadModule(config.renderer)
const response = await render(JSON.parse(await readableToString(req)))
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(response))
})
},
}
}
1 change: 1 addition & 0 deletions packages/svelte/src/server.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from '@inertiajs/core/server'
export { default as default } from '@inertiajs/core/server'
155 changes: 1 addition & 154 deletions playgrounds/svelte5/app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

namespace App\Providers;

use Illuminate\Foundation\Vite;
use Illuminate\Support\HtmlString;
use Illuminate\Support\Js;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
Expand All @@ -16,157 +13,7 @@ class AppServiceProvider extends ServiceProvider
*/
public function register()
{
$this->app->singleton(Vite::class, fn () => new class extends Vite
{
/**
* The prefetching strategy to use.
*
* @var 'waterfall'|'aggressive'
*/
protected $prefetchStrategy = 'waterfall';

/**
* When using the "waterfall" strategy, the count of assets to load at one time.
*
* @param int
*/
protected $prefetchChunks = 3;

/**
* Set the prefetching strategy.
*
* @param 'waterfall'|'aggressive' $strategy
* @param ...mixed $config
*/
public function usePrefetchStrategy(string $strategy, mixed ...$config): static
{
$this->prefetchStrategy = $strategy;

if ($strategy === 'waterfall') {
$this->prefetchChunks = $config[0] ?? 3;
}

return $this;
}

/**
* Generate Vite tags for an entrypoint.
*
* @param string|string[] $entrypoints
* @param string|null $buildDirectory
* @return \Illuminate\Support\HtmlString
*/
public function __invoke($entrypoints, $buildDirectory = null)
{
$manifest = $this->manifest($buildDirectory ??= $this->buildDirectory);
$base = parent::__invoke($entrypoints, $buildDirectory);

if ($this->isRunningHot()) {
return $base;
}

return collect($entrypoints)
->flatMap(fn ($entrypoint) => collect($manifest[$entrypoint]['dynamicImports'] ?? [])
->map(fn ($import) => $manifest[$import])
->filter(fn ($chunk) => str_ends_with($chunk['file'], '.js') || str_ends_with($chunk['file'], '.css'))
->flatMap($resolveImportChunks = function ($chunk) use (&$resolveImportChunks, $manifest) {
return collect([...$chunk['imports'] ?? [], ...$chunk['dynamicImports'] ?? []])
->reduce(
fn ($chunks, $import) => $chunks->merge(
$resolveImportChunks($manifest[$import])
),
collect([$chunk])
)
->merge(collect($chunk['css'] ?? [])->map(
fn ($css) => collect($manifest)->first(fn ($chunk) => $chunk['file'] === $css) ?? [
'file' => $css,
],
));
})
->map(function ($chunk) use ($buildDirectory, $manifest) {
return collect([
...$this->resolvePreloadTagAttributes(
$chunk['src'] ?? null,
$url = $this->assetPath("{$buildDirectory}/{$chunk['file']}"),
$chunk,
$manifest,
),
'rel' => 'prefetch',
'href' => $url,
])->reject(
fn ($value) => in_array($value, [null, false], true)
)->mapWithKeys(fn ($value, $key) => [
$key = (is_int($key) ? $value : $key) => $value === true ? $key : $value,
])->all();
})
->reject(fn ($attributes) => isset($this->preloadedAssets[$attributes['href']])))
->unique('href')
->values()
->pipe(fn ($assets) => with(Js::from($assets), fn ($assets) => match ($this->prefetchStrategy) {
'waterfall' => new HtmlString($base.<<<HTML
<script>
window.addEventListener('load', () => window.setTimeout(() => {
const linkTemplate = document.createElement('link')
linkTemplate.rel = 'prefetch'
const makeLink = (asset) => {
const link = linkTemplate.cloneNode()
Object.keys(asset).forEach((attribute) => {
link.setAttribute(attribute, asset[attribute])
})
return link
}
const loadNext = (assets, count) => window.setTimeout(() => {
const fragment = new DocumentFragment
while (count > 0) {
const link = makeLink(assets.shift())
fragment.append(link)
count--
if (assets.length) {
link.onload = () => loadNext(assets, 1)
link.error = () => loadNext(assets, 1)
}
}
document.head.append(fragment)
})
loadNext({$assets}, {$this->prefetchChunks})
}))
</script>
HTML),
'aggressive' => new HtmlString($base.<<<HTML
<script>
window.addEventListener('load', () => window.setTimeout(() => {
const linkTemplate = document.createElement('link')
linkTemplate.rel = 'prefetch'
const makeLink = (asset) => {
const link = linkTemplate.cloneNode()
Object.keys(asset).forEach((attribute) => {
link.setAttribute(attribute, asset[attribute])
})
return link
}
const fragment = new DocumentFragment
{$assets}.forEach((asset) => fragment.append(makeLink(asset)))
document.head.append(fragment)
}))
</script>
HTML),
}));
}
});
//
}

/**
Expand Down
3 changes: 1 addition & 2 deletions playgrounds/svelte5/resources/js/ssr.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { createInertiaApp } from '@inertiajs/svelte'
import { createInertiaApp, type ResolvedComponent } from '@inertiajs/svelte'
import createServer from '@inertiajs/svelte/server'
import { ResolvedComponent } from '@inertiajs/svelte'

createServer((page) =>
createInertiaApp({
Expand Down
8 changes: 8 additions & 0 deletions playgrounds/svelte5/resources/js/viteSsr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createInertiaApp } from '@inertiajs/svelte'
import type { AppCallback } from '@inertiajs/svelte/server'

export const render: AppCallback = (page) =>
createInertiaApp({
page,
resolve: (name) => import(`./Pages/${name}.svelte`),
})
4 changes: 4 additions & 0 deletions playgrounds/svelte5/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { svelte } from '@sveltejs/vite-plugin-svelte'
import laravel from 'laravel-vite-plugin'
import { defineConfig } from 'vite'
import inertia from '@inertiajs/core/vite';

export default defineConfig({
plugins: [
Expand All @@ -9,6 +10,9 @@ export default defineConfig({
ssr: 'resources/js/ssr.ts',
refresh: true,
}),
inertia({
renderer: 'resources/js/viteSsr.ts',
}),
svelte(),
],
})

0 comments on commit 76bf077

Please sign in to comment.