mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-29 09:02:03 +00:00
feat: ssr with service worker
This commit is contained in:
parent
e6fa415e5a
commit
2dbaae6b7d
@ -3,12 +3,12 @@ import { rollup, OutputOptions } from 'rollup'
|
|||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
import Hookable from 'hookable'
|
import Hookable from 'hookable'
|
||||||
import defu from 'defu'
|
import defu from 'defu'
|
||||||
import { readFile, writeFile, existsSync } from 'fs-extra'
|
import { existsSync, copy, emptyDir } from 'fs-extra'
|
||||||
import prettyBytes from 'pretty-bytes'
|
import prettyBytes from 'pretty-bytes'
|
||||||
import gzipSize from 'gzip-size'
|
import gzipSize from 'gzip-size'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import { getRollupConfig } from './rollup.config'
|
import { getRollupConfig } from './rollup.config'
|
||||||
import { tryImport, hl, prettyPath } from './utils'
|
import { tryImport, hl, prettyPath, compileTemplateToJS, renderTemplate } from './utils'
|
||||||
|
|
||||||
async function main () {
|
async function main () {
|
||||||
const rootDir = resolve(process.cwd(), process.argv[2] || '.')
|
const rootDir = resolve(process.cwd(), process.argv[2] || '.')
|
||||||
@ -18,15 +18,23 @@ async function main () {
|
|||||||
rootDir,
|
rootDir,
|
||||||
buildDir: '',
|
buildDir: '',
|
||||||
targets: [],
|
targets: [],
|
||||||
|
templates: [],
|
||||||
nuxt: 2,
|
nuxt: 2,
|
||||||
target: process.argv[3] && process.argv[3][0] !== '-' ? process.argv[3] : null,
|
target: process.argv[3] && process.argv[3][0] !== '-' ? process.argv[3] : null,
|
||||||
minify: process.argv.includes('--minify') ? true : null,
|
minify: process.argv.includes('--minify') ? true : null,
|
||||||
analyze: process.argv.includes('--analyze') ? true : null,
|
analyze: process.argv.includes('--analyze') ? true : null,
|
||||||
logStartup: true
|
logStartup: true
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(config, tryImport(rootDir, './nuxt.config')!.deploy)
|
Object.assign(config, tryImport(rootDir, './nuxt.config')!.deploy)
|
||||||
|
|
||||||
config.buildDir = resolve(config.rootDir, config.buildDir || '.nuxt')
|
config.buildDir = resolve(config.rootDir, config.buildDir || '.nuxt')
|
||||||
|
|
||||||
|
config.targets = config.targets.map(t => typeof t === 'string' ? { target: t } : t)
|
||||||
|
if (config.target && !config.targets.find(t => t.target === config.target)) {
|
||||||
|
config.targets.push({ target: config.target })
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure dist exists
|
// Ensure dist exists
|
||||||
if (!existsSync(resolve(config.buildDir, 'dist/server'))) {
|
if (!existsSync(resolve(config.buildDir, 'dist/server'))) {
|
||||||
return consola.error('Please use `nuxt build` first to build project!')
|
return consola.error('Please use `nuxt build` first to build project!')
|
||||||
@ -40,10 +48,7 @@ async function main () {
|
|||||||
// Compile html template
|
// Compile html template
|
||||||
const htmlTemplateFile = resolve(config.buildDir, `views/${{ 2: 'app', 3: 'document' }[config.nuxt]}.template.html`)
|
const htmlTemplateFile = resolve(config.buildDir, `views/${{ 2: 'app', 3: 'document' }[config.nuxt]}.template.html`)
|
||||||
const htmlTemplateFileJS = htmlTemplateFile.replace(/.html$/, '.js').replace('app.', 'document.')
|
const htmlTemplateFileJS = htmlTemplateFile.replace(/.html$/, '.js').replace('app.', 'document.')
|
||||||
const htmlTemplateContents = await readFile(htmlTemplateFile, 'utf-8')
|
await compileTemplateToJS(htmlTemplateFile, htmlTemplateFileJS)
|
||||||
// eslint-disable-next-line no-template-curly-in-string
|
|
||||||
const htmlTemplateCompiled = `export default (params) => \`${htmlTemplateContents.replace(/{{ (\w+) }}/g, '${params.$1}')}\``
|
|
||||||
await writeFile(htmlTemplateFileJS, htmlTemplateCompiled)
|
|
||||||
consola.info('Generated', prettyPath(htmlTemplateFileJS))
|
consola.info('Generated', prettyPath(htmlTemplateFileJS))
|
||||||
|
|
||||||
// Bundle for each target
|
// Bundle for each target
|
||||||
@ -59,9 +64,14 @@ async function main () {
|
|||||||
consola.info(`Generating bundle for ${hl(target.target)}`)
|
consola.info(`Generating bundle for ${hl(target.target)}`)
|
||||||
|
|
||||||
const _config: any = defu(
|
const _config: any = defu(
|
||||||
|
// Target specific config by user
|
||||||
target,
|
target,
|
||||||
|
// Global user config
|
||||||
config,
|
config,
|
||||||
tryImport(__dirname, `./targets/${target.target}`) || tryImport(config.rootDir, target.target)
|
// Target defaults
|
||||||
|
tryImport(__dirname, `./targets/${target.target}`) || tryImport(config.rootDir, target.target),
|
||||||
|
// Generic defaults
|
||||||
|
{ outDir: resolve(config.buildDir, `dist/${config.target}`), outName: 'index.js' }
|
||||||
)
|
)
|
||||||
|
|
||||||
const hooks = new Hookable()
|
const hooks = new Hookable()
|
||||||
@ -69,6 +79,8 @@ async function main () {
|
|||||||
|
|
||||||
await hooks.callHook('config', _config)
|
await hooks.callHook('config', _config)
|
||||||
|
|
||||||
|
emptyDir(_config.outDir)
|
||||||
|
|
||||||
_config.rollupConfig = getRollupConfig(_config)
|
_config.rollupConfig = getRollupConfig(_config)
|
||||||
await hooks.callHook('rollup:before', _config)
|
await hooks.callHook('rollup:before', _config)
|
||||||
const build = await rollup(_config.rollupConfig)
|
const build = await rollup(_config.rollupConfig)
|
||||||
@ -79,6 +91,20 @@ async function main () {
|
|||||||
consola.success('Generated', prettyPath((_config.rollupConfig.output as any).file),
|
consola.success('Generated', prettyPath((_config.rollupConfig.output as any).file),
|
||||||
chalk.gray(`(Size: ${size} Gzip: ${zSize})`)
|
chalk.gray(`(Size: ${size} Gzip: ${zSize})`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for (const tmpl of _config.templates) {
|
||||||
|
const dstPath = resolve(_config.outDir, tmpl.dst)
|
||||||
|
await renderTemplate(tmpl.src, dstPath, { config: _config })
|
||||||
|
consola.info('Compiled', prettyPath(dstPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_config.copyAssets) {
|
||||||
|
const publicDir = typeof _config.copyAssets === 'string' ? _config.copyAssets : 'public'
|
||||||
|
const dst = resolve(_config.outDir, publicDir, '_nuxt')
|
||||||
|
await copy(resolve(_config.buildDir, 'dist/client'), dst)
|
||||||
|
consola.info('Copied public assets to', prettyPath(dst))
|
||||||
|
}
|
||||||
|
|
||||||
await hooks.callHook('done', _config)
|
await hooks.callHook('done', _config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ export const getRollupConfig = (config) => {
|
|||||||
const options: RollupConfig = {
|
const options: RollupConfig = {
|
||||||
input: config.entry,
|
input: config.entry,
|
||||||
output: {
|
output: {
|
||||||
file: path.resolve(config.buildDir, `dist/${config.target}`, 'index.js'),
|
file: path.resolve(config.outDir, config.outName),
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
intro: '',
|
intro: '',
|
||||||
outro: '',
|
outro: '',
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { relative } from 'path'
|
|
||||||
import consola from 'consola'
|
import consola from 'consola'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
entry: require.resolve('./entry'),
|
entry: require.resolve('./entry'),
|
||||||
hooks: {
|
hooks: {
|
||||||
'done' ({ rollupConfig }) {
|
'done' ({ rollupConfig }) {
|
||||||
consola.info(`Usage: \`node ${relative(process.cwd(), rollupConfig.output.file)} [route]\``)
|
consola.info(`Usage: \`node ${rollupConfig.output.file} [route]\``)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
packages/nitro/src/targets/sw/entry.ts
Normal file
21
packages/nitro/src/targets/sw/entry.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { render } from '~runtime/server'
|
||||||
|
|
||||||
|
addEventListener('fetch', (event: any) => {
|
||||||
|
const url = new URL(event.request.url)
|
||||||
|
|
||||||
|
if (url.pathname.startsWith('/_nuxt') || url.pathname.includes('.') /* is file :} */) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event.respondWith(handleEvent(url, event.request))
|
||||||
|
})
|
||||||
|
|
||||||
|
async function handleEvent (url, request) {
|
||||||
|
try {
|
||||||
|
const { html, status, headers } = await render(url.pathname, { req: request })
|
||||||
|
return new Response(html, { status, headers })
|
||||||
|
} catch (error) {
|
||||||
|
return new Response('Internal Error: ' + error, { status: 500 })
|
||||||
|
}
|
||||||
|
}
|
20
packages/nitro/src/targets/sw/index.html
Normal file
20
packages/nitro/src/targets/sw/index.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
Loading...
|
||||||
|
<script>
|
||||||
|
if (!('serviceWorker' in navigator)) {
|
||||||
|
throw new Error('Browser not supported!')
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
navigator.serviceWorker.register('/nuxt.sw.js').then((registration) => {
|
||||||
|
console.log('ServiceWorker registration successful with scope:', registration.scope)
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('ServiceWorker registration failed:', error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
29
packages/nitro/src/targets/sw/index.ts
Normal file
29
packages/nitro/src/targets/sw/index.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { resolve } from 'path'
|
||||||
|
import consola from 'consola'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
entry: require.resolve('./entry'),
|
||||||
|
node: false,
|
||||||
|
copyAssets: '.',
|
||||||
|
outName: 'nuxt.sw.js',
|
||||||
|
templates: [
|
||||||
|
{ src: resolve(__dirname, 'index.html'), dst: 'index.html' },
|
||||||
|
{ src: resolve(__dirname, 'index.html'), dst: '200.html' }
|
||||||
|
],
|
||||||
|
hooks: {
|
||||||
|
config (config) {
|
||||||
|
if (config.nuxt === 2) {
|
||||||
|
config.renderer = 'vue2.basic'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'rollup:before' ({ rollupConfig }) {
|
||||||
|
rollupConfig.output.intro =
|
||||||
|
'const global = {}; const exports = {}; const module = { exports }; const process = { env: {}, hrtime: () => [0,0]};' +
|
||||||
|
rollupConfig.output.intro
|
||||||
|
rollupConfig.output.format = 'iife'
|
||||||
|
},
|
||||||
|
done ({ outDir }) {
|
||||||
|
consola.info(`Try with \`npx serve ${outDir}\``)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,34 @@
|
|||||||
import { relative } from 'path'
|
import { relative, dirname } from 'path'
|
||||||
|
import { readFile, writeFile, mkdirp } from 'fs-extra'
|
||||||
import jiti from 'jiti'
|
import jiti from 'jiti'
|
||||||
|
|
||||||
const pwd = process.cwd()
|
const pwd = process.cwd()
|
||||||
|
|
||||||
export const hl = str => '`' + str + '`'
|
export const hl = str => '`' + str + '`'
|
||||||
|
|
||||||
export const prettyPath = (p, highlight = true) => {
|
export function prettyPath (p, highlight = true) {
|
||||||
p = relative(pwd, p)
|
p = relative(pwd, p)
|
||||||
return highlight ? hl(p) : p
|
return highlight ? hl(p) : p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function loadTemplate (src) {
|
||||||
|
const contents = await readFile(src, 'utf-8')
|
||||||
|
return params => contents.replace(/{{ (\w+) }}/g, `${params.$1}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function renderTemplate (src, dst: string, params: any) {
|
||||||
|
const tmpl = await loadTemplate(src)
|
||||||
|
const rendered = tmpl(params)
|
||||||
|
await mkdirp(dirname(dst))
|
||||||
|
await writeFile(dst, rendered)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function compileTemplateToJS (src: string, dst: string) {
|
||||||
|
const contents = await readFile(src, 'utf-8')
|
||||||
|
// eslint-disable-next-line no-template-curly-in-string
|
||||||
|
const compiled = `export default (params) => \`${contents.replace(/{{ (\w+) }}/g, '${params.$1}')}\``
|
||||||
|
await mkdirp(dirname(dst))
|
||||||
|
await writeFile(dst, compiled)
|
||||||
|
}
|
||||||
|
|
||||||
export const tryImport = (dir, path) => { try { return jiti(dir)(path) } catch (_err) { } }
|
export const tryImport = (dir, path) => { try { return jiti(dir)(path) } catch (_err) { } }
|
||||||
|
Loading…
Reference in New Issue
Block a user