From ad4fc18ab8b1fa516c9d9d433a01366c358b2b6c Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 2 Nov 2020 15:42:27 +0100 Subject: [PATCH] feat: dynamic chunk importer --- packages/nitro/src/dynamic.ts | 44 +++++++++++++++++++++++++++++ packages/nitro/src/nuxt-deploy.ts | 12 +++++++- packages/nitro/src/rollup.config.ts | 28 +++++++++--------- packages/nitro/src/utils.ts | 4 ++- 4 files changed, 71 insertions(+), 17 deletions(-) create mode 100644 packages/nitro/src/dynamic.ts diff --git a/packages/nitro/src/dynamic.ts b/packages/nitro/src/dynamic.ts new file mode 100644 index 0000000000..2209ce7519 --- /dev/null +++ b/packages/nitro/src/dynamic.ts @@ -0,0 +1,44 @@ +import { join } from 'path' +import globby from 'globby' + +export async function createDynamicImporter (cwd: string) { + const assets = await globby('**/*.js', { cwd, absolute: false }) + const chunkInfo = {} + for (const asset of assets) { + const { id, ids, modules } = require(join(cwd, asset)) + if ((id || ids) && modules) { + chunkInfo[asset] = { id, ids, modules: Object.keys(modules) } + } + } + + return (syncImport, asyncImport) => genRequireDynamic(chunkInfo, syncImport, asyncImport) +} + +export function genRequireDynamic (chunkInfo, syncImport, asyncImport) { + return `\nconst _dynamic_chunks = ${JSON.stringify(chunkInfo, null, 2)}; +function requireDynamic (chunkId) { + const chunk = _dynamic_chunks[chunkId] + + if (!chunk) { + return ${syncImport} + } + + const promise = ${asyncImport} + + if (Array.isArray(chunk.modules)) { + const modules = {} + for (const id of chunk.modules) { + modules[id] = function (module, _exports, _require) { + module.exports = promise.then(chunk => { + const asyncModule = { exports: {}, require: _require } + chunk.modules[id](asyncModule, asyncModule.exports, asyncModule.require) + return asyncModule.exports + }) + } + } + chunk.modules = modules + } + + return chunk +};` +} diff --git a/packages/nitro/src/nuxt-deploy.ts b/packages/nitro/src/nuxt-deploy.ts index 8148e7f959..bb4ee92788 100644 --- a/packages/nitro/src/nuxt-deploy.ts +++ b/packages/nitro/src/nuxt-deploy.ts @@ -9,6 +9,7 @@ import gzipSize from 'gzip-size' import chalk from 'chalk' import { getRollupConfig } from './rollup.config' import { tryImport, hl, prettyPath } from './utils' +import { createDynamicImporter } from './dynamic' async function main () { const rootDir = resolve(process.cwd(), process.argv[2] || '.') @@ -19,6 +20,9 @@ async function main () { buildDir: '', targets: [], nuxt: 2, + dynamicImporter: undefined, + importSync: "require('../server/' + chunkId)", + importAsync: "Promise.resolve(require('../server/' + chunkId))", 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, @@ -44,7 +48,13 @@ async function main () { // 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)) + + // Collect dynamic chunks + if (!config.dynamicImporter) { + consola.info('Collecting dynamic chunks...') + config.dynamicImporter = await createDynamicImporter(resolve(config.buildDir, 'dist/server')) + } // Bundle for each target for (let target of config.targets) { diff --git a/packages/nitro/src/rollup.config.ts b/packages/nitro/src/rollup.config.ts index 33649243cf..ede9cdc4fb 100644 --- a/packages/nitro/src/rollup.config.ts +++ b/packages/nitro/src/rollup.config.ts @@ -53,6 +53,12 @@ export const getRollupConfig = (config) => { plugins: [] } + if (config.logStartup) { + options.output.intro += 'global._startTime = process.hrtime();' + // eslint-disable-next-line no-template-curly-in-string + options.output.outro += 'global._endTime = process.hrtime(global._startTime); global._coldstart = ((_endTime[0] * 1e9) + _endTime[1]) / 1e6; console.log(`λ Cold start took: ${global._coldstart}ms`);' + } + // https://github.com/rollup/plugins/tree/master/packages/replace options.plugins.push(replace({ values: { @@ -60,15 +66,13 @@ export const getRollupConfig = (config) => { } })) - // Preserve dynamic require - // https://github.com/rollup/plugins/tree/master/packages/replace - options.output.intro += 'const requireDynamic = require;' - options.plugins.push(replace({ - values: { - 'require("./" +': 'requireDynamic("../server/" +' - }, - delimiters: ['', ''] - })) + // Dynamic Importer + if (config.dynamicImporter) { + options.output.intro += config.dynamicImporter(config.importSync, config.importAsync) + } else { + options.output.intro += 'const requireDynamic = require;' + } + options.plugins.push(replace({ values: { 'require("./" +': 'requireDynamic(' }, delimiters: ['', ''] })) // https://github.com/rollup/plugins/tree/master/packages/alias options.plugins.push(alias({ @@ -109,12 +113,6 @@ export const getRollupConfig = (config) => { // https://github.com/rollup/plugins/tree/master/packages/json options.plugins.push(json()) - if (config.logStartup) { - options.output.intro += 'global._startTime = process.hrtime();' - // eslint-disable-next-line no-template-curly-in-string - options.output.outro += 'global._endTime = process.hrtime(global._startTime); global._coldstart = ((_endTime[0] * 1e9) + _endTime[1]) / 1e6; console.log(`λ Cold start took: ${global._coldstart}ms`);' - } - if (config.analyze) { // https://github.com/doesdev/rollup-plugin-analyzer options.plugins.push(analyze()) diff --git a/packages/nitro/src/utils.ts b/packages/nitro/src/utils.ts index 510d3fb00e..9994a57d4b 100644 --- a/packages/nitro/src/utils.ts +++ b/packages/nitro/src/utils.ts @@ -1,10 +1,12 @@ import { relative } from 'path' import jiti from 'jiti' +const pwd = process.cwd() + export const hl = str => '`' + str + '`' export const prettyPath = (p, highlight = true) => { - p = relative(process.cwd(), p) + p = relative(pwd, p) return highlight ? hl(p) : p }