Skip to content

Commit cb2824d

Browse files
authored
feat: New vite plugin config: useRelativePaths (#283)
* Add unit tests for findCommonPath so I know what its doing * Add "useRelativePaths" option to vite plugin * useRelativePaths in react examples * Add docs for useRelativePaths
1 parent c1293dd commit cb2824d

File tree

12 files changed

+284
-158
lines changed

12 files changed

+284
-158
lines changed

docs/guide/build-and-deploy.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ The `viteFastify` plugin can be given the following options:
5151

5252
* `spa` - Set this to `true` to disable the SSR build entirely. Default: `false`.
5353
* `clientModule` - The location of the SSR entry point, relative to the index.html file. Defaults to `index.js`. You can also use an absolute path to be extra safe.
54+
* `useRelativePaths` - Set this to `true` to avoid saving absolute paths in the `vite.config.json` file. This is useful if the machine you run the build on is NOT the machine you plan to run the server on (for example: if you build on a local machine but then copy the results into a Docker container). This results in a more hermetic application distribution. In a future release, this will be defaulted to `true`.
5455

5556
Assuming you do not need to build your Fastify server itself, the only build script you need in your `package.json` file is below:
5657

examples/react-vanilla-spa-ts/vite.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import viteReact from '@vitejs/plugin-react'
55
export default {
66
root: resolve(import.meta.dirname, 'src', 'client'),
77
plugins: [
8-
viteFastify({ spa: true }),
8+
viteFastify({ spa: true, useRelativePaths: true }),
99
viteReact()
1010
],
1111
build: {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
html {
2+
background: #f0e5d3;
3+
}
4+
* {
5+
font-size: 1.2em;
6+
font-family: sans-serif;
7+
}
8+
button {
9+
margin: 0 0.5em;
10+
}
11+
#root {
12+
display: flex;
13+
place-items: center;
14+
flex-direction: column;
15+
}

examples/react-vanilla-spa/vite.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import viteReact from '@vitejs/plugin-react'
55
export default {
66
root: join(import.meta.dirname, 'client'),
77
plugins: [
8-
viteFastify({ spa: true }),
8+
viteFastify({ spa: true, useRelativePaths: false }),
99
viteReact()
1010
],
1111
}

examples/react-vanilla-ts/vite.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import viteReact from '@vitejs/plugin-react'
55
export default {
66
root: resolve(import.meta.dirname, 'src', 'client'),
77
plugins: [
8-
viteFastify(),
8+
viteFastify({ useRelativePaths: true }),
99
viteReact()
1010
],
1111
build: {

examples/react-vanilla/vite.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import viteReact from '@vitejs/plugin-react'
55
export default {
66
root: join(import.meta.dirname, 'client'),
77
plugins: [
8-
viteFastify(),
8+
viteFastify({ useRelativePaths: true }),
99
viteReact()
1010
],
1111
}

packages/fastify-vite/config.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,13 +317,22 @@ async function resolveViteConfig(root, dev, { spa, distDir } = {}) {
317317
]
318318
}
319319

320+
async function determineOutDirRoot(vite) {
321+
const { usePathsRelativeToAppRoot } = vite.fastify
322+
if (usePathsRelativeToAppRoot) {
323+
const { packageDirectory } = await import('package-directory')
324+
return await packageDirectory()
325+
}
326+
return vite.root
327+
}
328+
320329
async function resolveSSRBundle({ dev, vite }) {
321330
const bundle = {}
322331
let clientOutDir
323332

324333
if (!dev) {
325334
if (vite.fastify) {
326-
clientOutDir = resolveIfRelative(vite.fastify.outDirs.client, vite.root)
335+
clientOutDir = resolveIfRelative(vite.fastify.outDirs.client, await determineOutDirRoot(vite))
327336
} else {
328337
// Backwards compatibility for projects that do not use the viteFastify plugin.
329338
bundle.dir = resolveIfRelative(vite.build.outDir, vite.root)
@@ -360,7 +369,7 @@ async function resolveSPABundle({ dev, vite }) {
360369
let clientOutDir
361370

362371
if (vite.fastify) {
363-
clientOutDir = resolveIfRelative(vite.fastify.outDirs.client, vite.root)
372+
clientOutDir = resolveIfRelative(vite.fastify.outDirs.client, await determineOutDirRoot(vite))
364373
} else {
365374
// Backwards compatibility for projects that do not use the viteFastify plugin.
366375
bundle.dir = resolveIfRelative(vite.build.outDir, vite.root)

packages/fastify-vite/mode/production.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,16 @@ async function setup(config) {
2323
let ssrOutDir
2424

2525
if (vite.fastify) {
26-
clientOutDir = resolveIfRelative(vite.fastify.outDirs.client, vite.root)
27-
ssrOutDir = resolveIfRelative(vite.fastify.outDirs.ssr || '', vite.root)
26+
const { outDirs, usePathsRelativeToAppRoot } = vite.fastify
27+
28+
let outDirRoot = vite.root
29+
if (usePathsRelativeToAppRoot) {
30+
const { packageDirectory } = await import('package-directory')
31+
outDirRoot = await packageDirectory()
32+
}
33+
34+
clientOutDir = resolveIfRelative(outDirs.client, outDirRoot)
35+
ssrOutDir = resolveIfRelative(outDirs.ssr || '', outDirRoot)
2836
} else {
2937
// Backwards compatibility for projects that do not use the viteFastify plugin.
3038
const outDir = resolveIfRelative(vite.build.outDir, vite.root)
@@ -37,11 +45,11 @@ async function setup(config) {
3745
const { assetsDir } = vite.build
3846

3947
if (!exists(clientOutDir)) {
40-
throw new Error('No client distribution bundle found.')
48+
throw new Error(`No client distribution bundle found at ${clientOutDir}.`)
4149
}
4250

4351
if (!spa && !exists(ssrOutDir)) {
44-
throw new Error('No SSR distribution bundle found.')
52+
throw new Error(`No SSR distribution bundle found at ${ssrOutDir}.`)
4553
}
4654

4755
// We also register fastify-static to serve all static files

packages/fastify-vite/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
"find-cache-dir": "^5.0.0",
5353
"fs-extra": "^11.3.0",
5454
"html-rewriter-wasm": "^0.4.1",
55-
"klaw": "^4.1.0"
55+
"klaw": "^4.1.0",
56+
"package-directory": "^8.1.0"
5657
},
5758
"devDependencies": {
5859
"@biomejs/biome": "^1.9.4",

packages/fastify-vite/plugin.mjs

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,46 @@
1+
import { isAbsolute, join, relative, sep } from 'node:path'
12
import getDeepMergeFunction from '@fastify/deepmerge'
2-
import { isAbsolute, join, write, sep } from './ioutils.cjs'
3+
import { writeFile } from 'node:fs/promises'
34

4-
export function viteFastify({ spa, clientModule } = {}) {
5-
let configuredOutDir
5+
export function viteFastify({ spa, clientModule, useRelativePaths = false } = {}) {
6+
let customOutDir
67
let jsonFilePath
78
let configToWrite = {}
89
let resolvedConfig = {}
910

1011
return {
1112
name: 'vite-fastify',
1213
enforce: 'pre',
13-
async config(config, { mode }) {
14-
configuredOutDir = config.build?.outDir
15-
const dev = mode === 'development'
16-
const outDir = configuredOutDir ?? 'dist'
14+
async config(rawConfig, { mode }) {
15+
customOutDir = rawConfig.build?.outDir
16+
const isDevMode = mode === 'development'
17+
const outDir = customOutDir ?? 'dist'
1718
const deepMerge = getDeepMergeFunction()
1819
const {
1920
resolveClientModule,
2021
createSSREnvironment,
2122
createClientEnvironment,
2223
} = await import('./config.js')
2324

24-
if (!config.environments) {
25-
config.environments = {}
25+
if (!rawConfig.environments) {
26+
rawConfig.environments = {}
2627
}
27-
config.environments.client = deepMerge(
28-
createClientEnvironment(dev, outDir),
29-
config.environments.client ?? {},
28+
rawConfig.environments.client = deepMerge(
29+
createClientEnvironment(isDevMode, outDir),
30+
rawConfig.environments.client ?? {},
3031
)
3132
if (!spa) {
32-
const ssrEntryPoint = clientModule ?? resolveClientModule(config.root)
33-
config.environments.ssr = deepMerge(
34-
createSSREnvironment(dev, outDir, ssrEntryPoint),
35-
config.environments.ssr ?? {},
33+
const ssrEntryPoint = clientModule ?? resolveClientModule(rawConfig.root)
34+
rawConfig.environments.ssr = deepMerge(
35+
createSSREnvironment(isDevMode, outDir, ssrEntryPoint),
36+
rawConfig.environments.ssr ?? {},
3637
)
37-
if (!config.builder) {
38-
config.builder = {}
38+
if (!rawConfig.builder) {
39+
rawConfig.builder = {}
3940
}
4041
// Write the JSON file after the bundle finishes writing to avoid getting deleted by emptyOutDir
41-
if (!config.builder.buildApp) {
42-
config.builder.buildApp = async (builder) => {
42+
if (!rawConfig.builder.buildApp) {
43+
rawConfig.builder.buildApp = async (builder) => {
4344
await builder.build(builder.environments.client)
4445
await builder.build(builder.environments.ssr)
4546
}
@@ -90,7 +91,12 @@ export function viteFastify({ spa, clientModule } = {}) {
9091
fastify,
9192
}
9293

93-
let commonDistFolder = configuredOutDir // respect custom build.outDir config if provided
94+
if (useRelativePaths) {
95+
await makeAllPathsRelative(configToWrite)
96+
fastify.usePathsRelativeToAppRoot = !!customOutDir
97+
}
98+
99+
let commonDistFolder = customOutDir // respect custom build.outDir config if provided
94100
if (!commonDistFolder) {
95101
const outDirs = Object.values(fastify.outDirs)
96102
commonDistFolder = outDirs.length > 1
@@ -106,7 +112,7 @@ export function viteFastify({ spa, clientModule } = {}) {
106112
}
107113
},
108114
async writeBundle() {
109-
await write(
115+
await writeFile(
110116
jsonFilePath,
111117
JSON.stringify(configToWrite, undefined, 2),
112118
'utf-8',
@@ -119,7 +125,7 @@ export function findCommonPath(paths) {
119125
if (paths.length === 1) {
120126
return paths[0]
121127
}
122-
const segments = paths.map((path) => path.split('/'))
128+
const segments = paths.map((path) => path.split(sep))
123129
const minLength = Math.min(...segments.map((arr) => arr.length))
124130
const commonSegments = []
125131
for (let i = 0; i < minLength; i++) {
@@ -131,7 +137,23 @@ export function findCommonPath(paths) {
131137
}
132138
}
133139

134-
return commonSegments.join('/')
140+
return commonSegments.join(sep)
141+
}
142+
143+
async function makeAllPathsRelative(viteConfig) {
144+
const { packageDirectory } = await import('package-directory')
145+
const applicationRootDirectory = await packageDirectory() // location of user's package.json
146+
const { build, fastify } = viteConfig
147+
148+
viteConfig.root = relative(applicationRootDirectory, viteConfig.root)
149+
150+
if (build?.outDir) {
151+
build.outDir = relative(applicationRootDirectory, build.outDir)
152+
}
153+
154+
Object.keys(fastify.outDirs).forEach((key) => {
155+
fastify.outDirs[key] = relative(applicationRootDirectory, fastify.outDirs[key])
156+
})
135157
}
136158

137159
export default viteFastify

0 commit comments

Comments
 (0)