diff --git a/packages/nitro/src/nuxt-deploy.ts b/packages/nitro/src/nuxt-deploy.ts index 63cc99e815..e304939d06 100644 --- a/packages/nitro/src/nuxt-deploy.ts +++ b/packages/nitro/src/nuxt-deploy.ts @@ -3,12 +3,12 @@ import { rollup, OutputOptions } from 'rollup' import consola from 'consola' import Hookable from 'hookable' import defu from 'defu' -import { readFile, writeFile, existsSync } from 'fs-extra' +import { existsSync, copy, emptyDir } from 'fs-extra' import prettyBytes from 'pretty-bytes' import gzipSize from 'gzip-size' import chalk from 'chalk' import { getRollupConfig } from './rollup.config' -import { tryImport, hl, prettyPath } from './utils' +import { tryImport, hl, prettyPath, compileTemplateToJS, renderTemplate } from './utils' async function main () { const rootDir = resolve(process.cwd(), process.argv[2] || '.') @@ -18,15 +18,23 @@ async function main () { rootDir, buildDir: '', targets: [], + templates: [], nuxt: 2, target: process.argv[3] && process.argv[3][0] !== '-' ? process.argv[3] : null, minify: process.argv.includes('--minify') ? true : null, analyze: process.argv.includes('--analyze') ? true : null, logStartup: true } + Object.assign(config, tryImport(rootDir, './nuxt.config')!.deploy) + 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 if (!existsSync(resolve(config.buildDir, 'dist/server'))) { return consola.error('Please use `nuxt build` first to build project!') @@ -40,10 +48,7 @@ async function main () { // Compile html template const htmlTemplateFile = resolve(config.buildDir, `views/${{ 2: 'app', 3: 'document' }[config.nuxt]}.template.html`) const htmlTemplateFileJS = htmlTemplateFile.replace(/.html$/, '.js').replace('app.', 'document.') - const htmlTemplateContents = await readFile(htmlTemplateFile, 'utf-8') - // eslint-disable-next-line no-template-curly-in-string - const htmlTemplateCompiled = `export default (params) => \`${htmlTemplateContents.replace(/{{ (\w+) }}/g, '${params.$1}')}\`` - await writeFile(htmlTemplateFileJS, htmlTemplateCompiled) + await compileTemplateToJS(htmlTemplateFile, htmlTemplateFileJS) consola.info('Generated', prettyPath(htmlTemplateFileJS)) // Bundle for each target @@ -59,9 +64,14 @@ async function main () { consola.info(`Generating bundle for ${hl(target.target)}`) const _config: any = defu( + // Target specific config by user target, + // Global user 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() @@ -69,6 +79,8 @@ async function main () { await hooks.callHook('config', _config) + emptyDir(_config.outDir) + _config.rollupConfig = getRollupConfig(_config) await hooks.callHook('rollup:before', _config) const build = await rollup(_config.rollupConfig) @@ -79,6 +91,20 @@ async function main () { consola.success('Generated', prettyPath((_config.rollupConfig.output as any).file), 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) } } diff --git a/packages/nitro/src/rollup.config.ts b/packages/nitro/src/rollup.config.ts index 5c0292e1b5..d7e0ce6f0b 100644 --- a/packages/nitro/src/rollup.config.ts +++ b/packages/nitro/src/rollup.config.ts @@ -44,7 +44,7 @@ export const getRollupConfig = (config) => { const options: RollupConfig = { input: config.entry, output: { - file: path.resolve(config.buildDir, `dist/${config.target}`, 'index.js'), + file: path.resolve(config.outDir, config.outName), format: 'cjs', intro: '', outro: '', diff --git a/packages/nitro/src/targets/cli/index.ts b/packages/nitro/src/targets/cli/index.ts index a1ecef69d7..4880495638 100644 --- a/packages/nitro/src/targets/cli/index.ts +++ b/packages/nitro/src/targets/cli/index.ts @@ -1,11 +1,11 @@ -import { relative } from 'path' + import consola from 'consola' export default { entry: require.resolve('./entry'), hooks: { 'done' ({ rollupConfig }) { - consola.info(`Usage: \`node ${relative(process.cwd(), rollupConfig.output.file)} [route]\``) + consola.info(`Usage: \`node ${rollupConfig.output.file} [route]\``) } } } diff --git a/packages/nitro/src/targets/sw/entry.ts b/packages/nitro/src/targets/sw/entry.ts new file mode 100644 index 0000000000..ffa520a9ea --- /dev/null +++ b/packages/nitro/src/targets/sw/entry.ts @@ -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 }) + } +} diff --git a/packages/nitro/src/targets/sw/index.html b/packages/nitro/src/targets/sw/index.html new file mode 100644 index 0000000000..1ee2757492 --- /dev/null +++ b/packages/nitro/src/targets/sw/index.html @@ -0,0 +1,20 @@ + + + + Loading... + + + + diff --git a/packages/nitro/src/targets/sw/index.ts b/packages/nitro/src/targets/sw/index.ts new file mode 100644 index 0000000000..c98c22a0b8 --- /dev/null +++ b/packages/nitro/src/targets/sw/index.ts @@ -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}\``) + } + } +} diff --git a/packages/nitro/src/utils.ts b/packages/nitro/src/utils.ts index 9994a57d4b..420fdfac67 100644 --- a/packages/nitro/src/utils.ts +++ b/packages/nitro/src/utils.ts @@ -1,13 +1,34 @@ -import { relative } from 'path' +import { relative, dirname } from 'path' +import { readFile, writeFile, mkdirp } from 'fs-extra' import jiti from 'jiti' const pwd = process.cwd() export const hl = str => '`' + str + '`' -export const prettyPath = (p, highlight = true) => { +export function prettyPath (p, highlight = true) { p = relative(pwd, 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) { } }