diff --git a/packages/nitro/src/context.ts b/packages/nitro/src/context.ts index 3e81fc3d9..188e46278 100644 --- a/packages/nitro/src/context.ts +++ b/packages/nitro/src/context.ts @@ -24,6 +24,7 @@ export interface SigmaContext { preset: string rollupConfig?: any renderer: string + serveStatic: boolean middleware: ServerMiddleware[] hooks: configHooksT nuxtHooks: configHooksT @@ -72,6 +73,7 @@ export function getsigmaContext (nuxtOptions: NuxtOptions, input: SigmaInput): S preset: undefined, rollupConfig: undefined, renderer: undefined, + serveStatic: false, middleware: [], ignore: [], env: {}, diff --git a/packages/nitro/src/presets/azure.ts b/packages/nitro/src/presets/azure.ts index 14bd9f4ce..e69e28c23 100644 --- a/packages/nitro/src/presets/azure.ts +++ b/packages/nitro/src/presets/azure.ts @@ -1,64 +1,21 @@ -import replace from '@rollup/plugin-replace' import archiver from 'archiver' import consola from 'consola' -import createEtag from 'etag' -import { createWriteStream, readdirSync, readFileSync, statSync } from 'fs-extra' -import mime from 'mime' -import { join, relative, resolve } from 'upath' - +import { createWriteStream } from 'fs-extra' +import { join, resolve } from 'upath' import { prettyPath, writeFile } from '../utils' import { SigmaPreset, SigmaContext } from '../context' export const azure: SigmaPreset = { inlineChunks: false, + serveStatic: true, entry: '{{ _internal.runtimeDir }}/entries/azure', hooks: { - 'sigma:rollup:before' (ctx: SigmaContext) { - const manifest = JSON.stringify(getStaticManifest(ctx)).replace(/\\"/g, '\\\\"') - ctx.rollupConfig.plugins.push(replace({ - values: { - 'process.env.STATIC_MANIFEST': `\`${manifest}\`` - } - })) - }, async 'sigma:compiled' (ctx: SigmaContext) { await writeRoutes(ctx) } } } -function getStaticManifest ({ output: { dir } }: SigmaContext) { - const files = [] - const staticRoot = resolve(dir, 'public') - - const addFiles = (directory: string) => { - const listing = readdirSync(directory) - listing.forEach((filename) => { - const fullPath = resolve(directory, filename) - if (statSync(fullPath).isDirectory()) { - return addFiles(fullPath) - } - files.push('/' + relative(staticRoot, fullPath)) - }) - } - - addFiles(staticRoot) - const metadata = files.reduce((metadata, filename) => { - let mimeType = mime.getType(filename) || 'text/plain' - if (mimeType.startsWith('text')) { - mimeType += '; charset=utf-8' - } - const etag = createEtag(readFileSync(join(staticRoot, filename))) - metadata[filename] = [mimeType, etag] - return metadata - }, {} as Record) - - return { - files, - metadata - } -} - function zipDirectory (dir: string, outfile: string): Promise { const archive = archiver('zip', { zlib: { level: 9 } }) const stream = createWriteStream(outfile) diff --git a/packages/nitro/src/presets/server.ts b/packages/nitro/src/presets/server.ts index 1d331c68f..617a78a45 100644 --- a/packages/nitro/src/presets/server.ts +++ b/packages/nitro/src/presets/server.ts @@ -7,7 +7,8 @@ export const server: SigmaPreset = extendPreset(node, { entry: '{{ _internal.runtimeDir }}/entries/server', externals: false, inlineChunks: false, - timing: true, + serveStatic: true, + minify: false, hooks: { 'sigma:compiled' ({ output }: SigmaContext) { consola.success('Ready to run', hl('node ' + prettyPath(output.serverDir))) diff --git a/packages/nitro/src/rollup/config.ts b/packages/nitro/src/rollup/config.ts index 748693819..5594d92b0 100644 --- a/packages/nitro/src/rollup/config.ts +++ b/packages/nitro/src/rollup/config.ts @@ -12,15 +12,16 @@ import analyze from 'rollup-plugin-analyzer' import type { Preset } from '@nuxt/un' import * as un from '@nuxt/un' -import hasha from 'hasha' import { SigmaContext } from '../context' import { resolvePath, MODULE_DIR } from '../utils' -import { dynamicRequire } from './dynamic-require' -import { externals } from './externals' -import { timing } from './timing' -import { autoMock } from './automock' -import esbuild from './esbuild' +import { dynamicRequire } from './plugins/dynamic-require' +import { externals } from './plugins/externals' +import { timing } from './plugins/timing' +import { autoMock } from './plugins/automock' +import { staticAssets } from './plugins/static' +import { middleware } from './plugins/middleware' +import { esbuild } from './plugins/esbuild' export type RollupConfig = InputOptions & { output: OutputOptions } @@ -125,28 +126,17 @@ export const getRollupConfig = (sigmaContext: SigmaContext) => { } })) - // https://github.com/rollup/plugins/tree/master/packages/replace - // TODO: better fix for node-fetch issue - rollupConfig.plugins.push(replace({ - delimiters: ['', ''], - values: { - 'require(\'encoding\')': '{}' - } - })) + // Static + if (sigmaContext.serveStatic) { + rollupConfig.plugins.push(staticAssets(sigmaContext)) + } - // Provide serverMiddleware - const getImportId = p => '_' + hasha(p).substr(0, 6) - rollupConfig.plugins.push(virtual({ - '~serverMiddleware': ` - ${sigmaContext.middleware.filter(m => m.lazy === false).map(m => `import ${getImportId(m.handle)} from '${m.handle}';`).join('\n')} - - ${sigmaContext.middleware.filter(m => m.lazy !== false).map(m => `const ${getImportId(m.handle)} = () => import('${m.handle}');`).join('\n')} - - export default [ - ${sigmaContext.middleware.map(m => `{ route: '${m.route}', handle: ${getImportId(m.handle)}, lazy: ${m.lazy || true}, promisify: ${m.promisify !== undefined ? m.promisify : true} }`).join(',\n')} - ]; - ` - })) + // Middleware + const _middleware = [...sigmaContext.middleware] + if (sigmaContext.serveStatic) { + _middleware.unshift({ route: '/', handle: '~runtime/server/static' }) + } + rollupConfig.plugins.push(middleware(_middleware)) // Polyfill rollupConfig.plugins.push(virtual({ diff --git a/packages/nitro/src/rollup/automock.ts b/packages/nitro/src/rollup/plugins/automock.ts similarity index 100% rename from packages/nitro/src/rollup/automock.ts rename to packages/nitro/src/rollup/plugins/automock.ts diff --git a/packages/nitro/src/rollup/dynamic-require.ts b/packages/nitro/src/rollup/plugins/dynamic-require.ts similarity index 100% rename from packages/nitro/src/rollup/dynamic-require.ts rename to packages/nitro/src/rollup/plugins/dynamic-require.ts diff --git a/packages/nitro/src/rollup/esbuild.ts b/packages/nitro/src/rollup/plugins/esbuild.ts similarity index 98% rename from packages/nitro/src/rollup/esbuild.ts rename to packages/nitro/src/rollup/plugins/esbuild.ts index 81723ae98..1a8566412 100644 --- a/packages/nitro/src/rollup/esbuild.ts +++ b/packages/nitro/src/rollup/plugins/esbuild.ts @@ -34,7 +34,7 @@ export type Options = { } } -export default (options: Options = {}): Plugin => { +export function esbuild (options: Options = {}): Plugin { let target: string | string[] const loaders = { diff --git a/packages/nitro/src/rollup/externals.ts b/packages/nitro/src/rollup/plugins/externals.ts similarity index 100% rename from packages/nitro/src/rollup/externals.ts rename to packages/nitro/src/rollup/plugins/externals.ts diff --git a/packages/nitro/src/rollup/plugins/middleware.ts b/packages/nitro/src/rollup/plugins/middleware.ts new file mode 100644 index 000000000..fa78b9862 --- /dev/null +++ b/packages/nitro/src/rollup/plugins/middleware.ts @@ -0,0 +1,21 @@ +import hasha from 'hasha' +import virtual from '@rollup/plugin-virtual' +import type { ServerMiddleware } from '../../context' + +export function middleware (middleware: ServerMiddleware[]) { + const getImportId = p => '_' + hasha(p).substr(0, 6) + + return virtual({ + '~serverMiddleware': ` +${middleware.filter(m => m.lazy === false).map(m => `import ${getImportId(m.handle)} from '${m.handle}';`).join('\n')} + +${middleware.filter(m => m.lazy !== false).map(m => `const ${getImportId(m.handle)} = () => import('${m.handle}');`).join('\n')} + +const middleware = [ + ${middleware.map(m => `{ route: '${m.route}', handle: ${getImportId(m.handle)}, lazy: ${m.lazy || true}, promisify: ${m.promisify !== undefined ? m.promisify : true} }`).join(',\n')} +]; + +export default middleware +` + }) +} diff --git a/packages/nitro/src/rollup/plugins/static.ts b/packages/nitro/src/rollup/plugins/static.ts new file mode 100644 index 000000000..9f27ef396 --- /dev/null +++ b/packages/nitro/src/rollup/plugins/static.ts @@ -0,0 +1,47 @@ +import createEtag from 'etag' +import { readFileSync, statSync } from 'fs-extra' +import mime from 'mime' +import { relative, resolve } from 'upath' +import virtual from '@rollup/plugin-virtual' +import globby from 'globby' +import type { SigmaContext } from '../../context' + +export function staticAssets (context: SigmaContext) { + const assets: Record = {} + + const files = globby.sync('**/*.*', { cwd: context.output.publicDir, absolute: false }) + + for (const id of files) { + let type = mime.getType(id) || 'text/plain' + if (type.startsWith('text')) { type += '; charset=utf-8' } + const fullPath = resolve(context.output.publicDir, id) + const etag = createEtag(readFileSync(fullPath)) + const stat = statSync(fullPath) + + assets[id] = { + type, + etag, + mtime: stat.mtime.toJSON(), + path: relative(context.output.serverDir, fullPath) + } + } + + return virtual({ + '~static-assets': `export default ${JSON.stringify(assets, null, 2)};`, + '~static': ` +import { readFile } from 'fs/promises' +import { resolve, dirname } from 'path' +import assets from '~static-assets' + +const mainDir = dirname(require.main.filename) + +export function readAsset (id) { + return readFile(resolve(mainDir, getAsset(id).path)) +} + +export function getAsset (id) { + return assets[id] +} +` + }) +} diff --git a/packages/nitro/src/rollup/timing.ts b/packages/nitro/src/rollup/plugins/timing.ts similarity index 100% rename from packages/nitro/src/rollup/timing.ts rename to packages/nitro/src/rollup/plugins/timing.ts