-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
improve: handle build/request so that delays are not necessary to mit…
…igate ENOENT preview server errors
- Loading branch information
1 parent
044d2b7
commit 96ba2c8
Showing
20 changed files
with
581 additions
and
289 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
export * from './plugin-build.js'; | ||
export { default } from './plugin-build.js'; | ||
export * from './plugin/build.js'; | ||
export { default } from './plugin/build.js'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { build, type InlineConfig, loadConfigFromFile, type LogLevel, mergeConfig } from 'vite'; | ||
|
||
import plugin from './plugin/build.js'; | ||
|
||
interface Options { | ||
readonly mode?: string; | ||
readonly config?: string; | ||
readonly logLevel?: LogLevel; | ||
readonly reload?: boolean; | ||
readonly clearScreen?: boolean; | ||
readonly base?: string; | ||
readonly outDir?: string; | ||
readonly host?: string | true; | ||
readonly port?: number; | ||
readonly strictPort?: true; | ||
readonly open?: string | true; | ||
} | ||
|
||
export const main = async (root: string | undefined, { | ||
config: configFile, | ||
mode = 'development', | ||
logLevel, | ||
reload, | ||
clearScreen, | ||
base, | ||
outDir, | ||
...preview | ||
}: Options): Promise<void> => { | ||
// Load the configuration manually so that the `env` is correct. | ||
const config: InlineConfig = mergeConfig<InlineConfig, InlineConfig>( | ||
await loadConfigFromFile( | ||
{ command: 'build', mode, isPreview: true, isSsrBuild: false }, | ||
configFile, | ||
root, | ||
logLevel, | ||
).then((value) => value?.config ?? {}), | ||
{ | ||
root, | ||
configFile: false, | ||
plugins: [plugin({ enable: true, reload })], | ||
logLevel, | ||
clearScreen, | ||
mode, | ||
base, | ||
build: { outDir }, | ||
preview, | ||
}, | ||
); | ||
|
||
await build(config); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import path from 'node:path'; | ||
|
||
import { type Connect } from 'vite'; | ||
|
||
import TEMPLATE_CLIENT_SCRIPT from '../template/client.js?raw'; | ||
import { createDebugger } from '../util/create-debugger.js'; | ||
|
||
interface Options { | ||
readonly base: string; | ||
} | ||
|
||
export const CLIENT_SCRIPT_NAME = 'vite-live-preview/client.ts'; | ||
|
||
/** | ||
* Middleware that serves the client script. | ||
*/ | ||
export default ({ base }: Options): Connect.NextHandleFunction => { | ||
const debug = createDebugger('live-preview'); | ||
const clientScript = TEMPLATE_CLIENT_SCRIPT.replace(/(?<=const base *= *)'\/'/u, JSON.stringify(base)); | ||
const clientScriptLength = Buffer.byteLength(clientScript, 'utf8'); | ||
const clientScriptRoute = path.posix.join(base, CLIENT_SCRIPT_NAME); | ||
|
||
return (req, res, next) => { | ||
if (req.url !== clientScriptRoute) return next(); | ||
|
||
res.setHeader('Content-Type', 'text/javascript'); | ||
res.setHeader('Content-Length', clientScriptLength); | ||
res.end(clientScript); | ||
debug?.('served client script.'); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { type Connect } from 'vite'; | ||
|
||
interface Options { | ||
readonly getPromise: () => Promise<void>; | ||
} | ||
|
||
/** | ||
* Middleware that delays the response until a promise resolves. | ||
*/ | ||
export default ({ getPromise }: Options): Connect.NextHandleFunction => { | ||
return (req, res, next) => { | ||
void getPromise().finally(() => next()); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import path from 'node:path'; | ||
|
||
import ansiHtml from 'ansi-html'; | ||
import { htmlEscape } from 'escape-goat'; | ||
import { type Connect } from 'vite'; | ||
|
||
import TEMPLATE_ERROR_HTML from '../template/error.html?raw'; | ||
import { createDebugger } from '../util/create-debugger.js'; | ||
import { CLIENT_SCRIPT_NAME } from './client.js'; | ||
|
||
interface Options { | ||
readonly base: string; | ||
readonly getError: () => Error | undefined; | ||
} | ||
|
||
/** | ||
* Middleware that serves an error page when an error is present. | ||
*/ | ||
export default ({ base, getError }: Options): Connect.NextHandleFunction => { | ||
const debug = createDebugger('live-preview'); | ||
const clientSrc = JSON.stringify(path.posix.join(base, CLIENT_SCRIPT_NAME)); | ||
|
||
return (req, res, next) => { | ||
if (!req.headers.accept?.includes('html')) return next(); | ||
|
||
const error = getError(); | ||
|
||
if (!error) return next(); | ||
|
||
const errorMessage = ansiHtml(htmlEscape(error.message)); | ||
const errorHtml = TEMPLATE_ERROR_HTML | ||
.replace(/(?=<\/head>)|$/iu, `<script crossorigin="" src=${clientSrc}></script>\n`) | ||
.replace(/(?=<\/body>)|$/iu, `<pre class="error"><code>${errorMessage}</code></pre>\n`); | ||
|
||
res.statusCode = 500; | ||
res.setHeader('Content-Type', 'text/html'); | ||
res.setHeader('Content-Length', Buffer.byteLength(errorHtml, 'utf8')); | ||
res.end(errorHtml); | ||
debug?.(`served error page for "${req.url}".`); | ||
}; | ||
}; |
Oops, something went wrong.