Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: simplify configuration for SPA applications #94

Merged
merged 1 commit into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/react-vanilla-spa/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
31 changes: 31 additions & 0 deletions examples/react-vanilla-spa/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
parser: '@babel/eslint-parser',
parserOptions: {
requireConfigFile: false,
ecmaVersion: 2021,
sourceType: 'module',
babelOptions: {
presets: ['@babel/preset-react'],
},
ecmaFeatures: {
jsx: true,
},
},
extends: [
'plugin:react/recommended',
'standard',
],
plugins: [
'react',
],
rules: {
'comma-dangle': ['error', 'always-multiline'],
'react/prop-types': 'off',
'import/no-absolute-path': 'off',
},
settings: {
react: {
version: '18.0',
},
},
}
7 changes: 7 additions & 0 deletions examples/react-vanilla-spa/client/base.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react'

export function createApp () {
return (
<p>Hello world from React and @fastify/vite!</p>
)
}
3 changes: 3 additions & 0 deletions examples/react-vanilla-spa/client/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<!DOCTYPE html>
<main><!-- element --></main>
<script type="module" src="/mount.js"></script>
5 changes: 5 additions & 0 deletions examples/react-vanilla-spa/client/mount.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createRoot } from 'react-dom/client'
import { createApp } from './base.jsx'

const root = createRoot(document.querySelector('main'))
root.render(createApp())
38 changes: 38 additions & 0 deletions examples/react-vanilla-spa/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"type": "module",
"scripts": {
"dev": "node server.js --dev",
"devinstall": "zx ../../devinstall.mjs react-vanilla -- node server.js --dev",
"build": "vite build --outDir dist/client",
"lint": "eslint . --ext .js,.jsx --fix",
"test": "vitest run"
},
"dependencies": {
"react-dom": "^18.1.0",
"fastify": "^4.7.0",
"vite": "^3.1.6",
"@fastify/static": "^6.5.0",
"fs-extra": "^10.0.0",
"klaw": "^4.0.1",
"middie": "^5.2.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.16.0",
"@babel/preset-react": "^7.16.0",
"@vitejs/plugin-react": "^1.3.2",
"eslint": "^7.32.0",
"eslint-config-standard": "^16.0.2",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-react": "^7.29.4"
},
"devInstall": {
"local": {
"@fastify/vite": "^3.0.1"
},
"external": {
"react-dom": "^18.1.0"
}
}
}
24 changes: 24 additions & 0 deletions examples/react-vanilla-spa/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Fastify from 'fastify'
import FastifyVite from '@fastify/vite'

export async function main (dev) {
const server = Fastify()

await server.register(FastifyVite, {
root: import.meta.url,
dev: dev ?? process.argv.includes('--dev'),
spa: true,
})

server.get('/', (req, reply) => {
reply.html()
})

await server.vite.ready()
return server
}

if (process.argv[1] === new URL(import.meta.url).pathname) {
const server = await main()
await server.listen({ port: 3000 })
}
6 changes: 6 additions & 0 deletions examples/react-vanilla-spa/server.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { test } from 'vitest'
import { makeIndexTest } from '../../testing.js'
import { main } from './server.js'

test('render index page in development', makeIndexTest({ main, dev: true }))
test('render index page in production', makeIndexTest({ main }))
14 changes: 14 additions & 0 deletions examples/react-vanilla-spa/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { resolve, dirname } from 'path'
import viteReact from '@vitejs/plugin-react'

const path = new URL(import.meta.url).pathname
const root = resolve(dirname(path), 'client')

const plugins = [
viteReact({ jsxRuntime: 'classic' }),
]

export default {
root,
plugins,
}
9 changes: 9 additions & 0 deletions examples/react-vanilla-spa/vitest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { dirname } from 'node:path'
import { defineConfig } from 'vitest/config'

export default defineConfig({
root: dirname(new URL(import.meta.url).pathname),
test: {
testTimeout: 15000,
},
})
21 changes: 19 additions & 2 deletions packages/fastify-vite/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ async function configure (options = {}) {
const root = resolveRoot(options.root)
const dev = typeof options.dev === 'boolean' ? options.dev : defaultConfig.dev
const [vite, viteConfig] = await resolveViteConfig(root, dev)
const resolveBundle = options.spa ? resolveSPABundle : resolveSSRBundle
const bundle = await resolveBundle({ dev, vite })
const config = Object.assign(defaultConfig, {
...options,
Expand Down Expand Up @@ -179,7 +180,7 @@ async function resolveViteConfig (root, dev) {
return [null, null]
}

async function resolveBundle ({ dev, vite }) {
async function resolveSSRBundle ({ dev, vite }) {
const bundle = {}
if (!dev) {
bundle.dir = resolve(vite.root, vite.build.outDir)
Expand All @@ -195,6 +196,21 @@ async function resolveBundle ({ dev, vite }) {
return bundle
}

async function resolveSPABundle ({ dev, vite }) {
const bundle = {}
if (!dev) {
bundle.dir = resolve(vite.root, vite.build.outDir)
const indexHtmlPath = resolve(bundle.dir, 'client/index.html')
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left this as /dist/client, but it may make sense to remove this hard dependency on having a /client folder. SPAs should probably just use the /dist output (or at least allow for configuration)

Any thoughts on that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSYK There's no hard dependency on having a client/ folder, root in vite.config.js can be the same folder as server.js and it should work fine.

Amazing work, I'll give it a go and review properly soon.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok cool thanks!

What I meant here was that AFAICT, the output folder has to be /client right now for production. Unless there is a way of overriding how the production bundles are loaded that I missed.

if (!exists(indexHtmlPath)) {
return
}
bundle.indexHtml = await read(indexHtmlPath, 'utf8')
} else {
bundle.manifest = []
}
return bundle
}

async function resolveBuildCommands (root, renderer) {
const [vite] = await resolveViteConfig(root)
return [
Expand All @@ -205,7 +221,8 @@ async function resolveBuildCommands (root, renderer) {

module.exports = {
configure,
resolveBundle,
resolveSSRBundle,
resolveSPABundle,
resolveBuildCommands,
}
module.exports.default = module.exports
9 changes: 7 additions & 2 deletions packages/fastify-vite/mode/production.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ async function setup (config) {

const clientDist = resolve(config.bundle.dir, 'client')
const serverDist = resolve(config.bundle.dir, 'server')
if (!exists(clientDist) || !exists(serverDist)) {
throw new Error('No distribution bundle found.')
if (!exists(clientDist)) {
throw new Error('No client distribution bundle found.')
}


if (!config.spa && !exists(serverDist)) {
throw new Error('No server distribution bundle found.')
}
// We also register fastify-static to serve all static files
// in production (dev server takes of this)
Expand Down