mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 00:23:53 +00:00
sigma (#36)
This commit is contained in:
parent
047761f8b7
commit
c06f09e9ab
@ -1,48 +1,97 @@
|
||||
import { resolve } from 'path'
|
||||
import { resolve, join } from 'upath'
|
||||
import consola from 'consola'
|
||||
import { rollup } from 'rollup'
|
||||
import Hookable from 'hookable'
|
||||
import { readFile, emptyDir } from 'fs-extra'
|
||||
import { rollup, watch as rollupWatch } from 'rollup'
|
||||
import ora from 'ora'
|
||||
import { readFile, emptyDir, copy } from 'fs-extra'
|
||||
import { printFSTree } from './utils/tree'
|
||||
import { getRollupConfig } from './rollup/config'
|
||||
import { hl, serializeTemplate, writeFile } from './utils'
|
||||
import { SLSOptions } from './config'
|
||||
import { SigmaContext } from './context'
|
||||
|
||||
export async function build (options: SLSOptions) {
|
||||
consola.info(`Generating bundle for ${hl(options.target)}`)
|
||||
export async function build (sigmaContext: SigmaContext) {
|
||||
consola.info(`Sigma preset is ${hl(sigmaContext.preset)}`)
|
||||
|
||||
const hooks = new Hookable()
|
||||
hooks.addHooks(options.hooks)
|
||||
|
||||
if (options.cleanTargetDir) {
|
||||
await emptyDir(options.targetDir)
|
||||
}
|
||||
// Cleanup output dir
|
||||
await emptyDir(sigmaContext.output.dir)
|
||||
|
||||
// Compile html template
|
||||
const htmlSrc = resolve(options.buildDir, `views/${{ 2: 'app', 3: 'document' }[2]}.template.html`)
|
||||
const htmlSrc = resolve(sigmaContext._nuxt.buildDir, `views/${{ 2: 'app', 3: 'document' }[2]}.template.html`)
|
||||
const htmlTemplate = { src: htmlSrc, contents: '', dst: '', compiled: '' }
|
||||
htmlTemplate.dst = htmlTemplate.src.replace(/.html$/, '.js').replace('app.', 'document.')
|
||||
htmlTemplate.contents = await readFile(htmlTemplate.src, 'utf-8')
|
||||
htmlTemplate.compiled = 'module.exports = ' + serializeTemplate(htmlTemplate.contents)
|
||||
await hooks.callHook('template:document', htmlTemplate)
|
||||
await sigmaContext._internal.hooks.callHook('sigma:template:document', htmlTemplate)
|
||||
await writeFile(htmlTemplate.dst, htmlTemplate.compiled)
|
||||
|
||||
options.rollupConfig = getRollupConfig(options)
|
||||
await generate(sigmaContext)
|
||||
|
||||
await hooks.callHook('rollup:before', options)
|
||||
sigmaContext.rollupConfig = getRollupConfig(sigmaContext)
|
||||
|
||||
const build = await rollup(options.rollupConfig).catch((error) => {
|
||||
error.message = '[serverless] Rollup Error: ' + error.message
|
||||
await sigmaContext._internal.hooks.callHook('sigma:rollup:before', sigmaContext)
|
||||
|
||||
return sigmaContext._nuxt.dev ? _watch(sigmaContext) : _build(sigmaContext)
|
||||
}
|
||||
|
||||
export async function generate (sigmaContext: SigmaContext) {
|
||||
await copy(
|
||||
resolve(sigmaContext._nuxt.buildDir, 'dist/client'),
|
||||
join(sigmaContext.output.publicDir, sigmaContext._nuxt.publicPath)
|
||||
)
|
||||
await copy(
|
||||
resolve(sigmaContext._nuxt.rootDir, sigmaContext._nuxt.staticDir),
|
||||
sigmaContext.output.publicDir
|
||||
)
|
||||
}
|
||||
|
||||
async function _build (sigmaContext: SigmaContext) {
|
||||
const spinner = ora()
|
||||
|
||||
spinner.start('Building server...')
|
||||
const build = await rollup(sigmaContext.rollupConfig).catch((error) => {
|
||||
spinner.fail('Rollup error: ' + error.messsage)
|
||||
throw error
|
||||
})
|
||||
|
||||
await build.write(options.rollupConfig.output)
|
||||
spinner.start('Wrting Sigma bundle...')
|
||||
await build.write(sigmaContext.rollupConfig.output)
|
||||
|
||||
await printFSTree(options.targetDir)
|
||||
|
||||
await hooks.callHook('done', options)
|
||||
spinner.succeed('Sigma built')
|
||||
await printFSTree(sigmaContext.output.serverDir)
|
||||
await sigmaContext._internal.hooks.callHook('sigma:compiled', sigmaContext)
|
||||
|
||||
return {
|
||||
entry: resolve(options.rollupConfig.output.dir, options.rollupConfig.output.entryFileNames)
|
||||
entry: resolve(sigmaContext.rollupConfig.output.dir, sigmaContext.rollupConfig.output.entryFileNames)
|
||||
}
|
||||
}
|
||||
|
||||
function _watch (sigmaContext: SigmaContext) {
|
||||
const spinner = ora()
|
||||
|
||||
const watcher = rollupWatch(sigmaContext.rollupConfig)
|
||||
|
||||
let start
|
||||
|
||||
watcher.on('event', (event) => {
|
||||
switch (event.code) {
|
||||
// The watcher is (re)starting
|
||||
case 'START':
|
||||
return
|
||||
|
||||
// Building an individual bundle
|
||||
case 'BUNDLE_START':
|
||||
start = Date.now()
|
||||
spinner.start('Building Sigma...')
|
||||
return
|
||||
|
||||
// Finished building all bundles
|
||||
case 'END':
|
||||
sigmaContext._internal.hooks.callHook('sigma:compiled', sigmaContext)
|
||||
return spinner.succeed(`Sigma built in ${Date.now() - start} ms`)
|
||||
|
||||
// Encountered an error while bundling
|
||||
case 'ERROR':
|
||||
spinner.fail('Rollup error: ' + event.error)
|
||||
// consola.error(event.error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,108 +0,0 @@
|
||||
import { resolve } from 'path'
|
||||
import defu from 'defu'
|
||||
import type { NuxtOptions } from '@nuxt/types'
|
||||
import Hookable, { configHooksT } from 'hookable'
|
||||
import { tryImport, resolvePath, detectTarget, extendTarget } from './utils'
|
||||
import * as TARGETS from './targets'
|
||||
|
||||
// eslint-disable-next-line
|
||||
export type UnresolvedPath = string | ((config: SLSOptions) => string)
|
||||
|
||||
export interface Nuxt extends Hookable{
|
||||
options: NuxtOptions
|
||||
}
|
||||
|
||||
export interface ServerMiddleware {
|
||||
route: string
|
||||
handle: string
|
||||
lazy?: boolean
|
||||
}
|
||||
|
||||
export interface SLSOptions {
|
||||
hooks: configHooksT
|
||||
nuxtHooks: configHooksT
|
||||
|
||||
rootDir: string
|
||||
buildDir: string
|
||||
publicDir: string
|
||||
routerBase: string
|
||||
publicPath: string
|
||||
fullStatic: boolean
|
||||
staticAssets: any
|
||||
|
||||
entry: UnresolvedPath
|
||||
outName: string
|
||||
node: false | true
|
||||
target: string
|
||||
minify: boolean
|
||||
externals: boolean
|
||||
rollupConfig?: any
|
||||
timing: boolean
|
||||
inlineChunks: boolean
|
||||
renderer: string
|
||||
analyze: boolean
|
||||
cleanTargetDir: boolean
|
||||
|
||||
runtimeDir: string
|
||||
slsDir: string
|
||||
targetDir: string
|
||||
|
||||
serverMiddleware: ServerMiddleware[],
|
||||
|
||||
static: string[]
|
||||
generateIgnore: string[]
|
||||
}
|
||||
|
||||
export interface SLSConfig extends Omit<Partial<SLSOptions>, 'targetDir'> {
|
||||
targetDir?: UnresolvedPath
|
||||
}
|
||||
|
||||
export type SLSTargetFn = (config: SLSConfig) => SLSConfig
|
||||
export type SLSTarget = SLSConfig | SLSTargetFn
|
||||
|
||||
export function getoptions (nuxtOptions: Nuxt['options'], serverless: SLSConfig): SLSOptions {
|
||||
const defaults: SLSConfig = {
|
||||
rootDir: nuxtOptions.rootDir,
|
||||
buildDir: nuxtOptions.buildDir,
|
||||
publicDir: nuxtOptions.generate.dir,
|
||||
routerBase: nuxtOptions.router.base,
|
||||
publicPath: nuxtOptions.build.publicPath,
|
||||
fullStatic: nuxtOptions.target === 'static' && !nuxtOptions._legacyGenerate,
|
||||
// @ts-ignore
|
||||
staticAssets: nuxtOptions.generate.staticAssets,
|
||||
|
||||
outName: '_nuxt.js',
|
||||
timing: true,
|
||||
inlineChunks: true,
|
||||
minify: false,
|
||||
externals: false,
|
||||
cleanTargetDir: true,
|
||||
|
||||
runtimeDir: resolve(__dirname, '../runtime'),
|
||||
slsDir: '{{ rootDir }}/.nuxt/serverless',
|
||||
targetDir: '{{ slsDir }}/{{ target }}',
|
||||
|
||||
serverMiddleware: serverless.serverMiddleware || [],
|
||||
|
||||
static: [],
|
||||
generateIgnore: []
|
||||
}
|
||||
|
||||
const target = serverless.target || process.env.NUXT_SLS_TARGET || detectTarget()
|
||||
let targetDefaults = TARGETS[target] || tryImport(nuxtOptions.rootDir, target)
|
||||
if (!targetDefaults) {
|
||||
throw new Error('Cannot resolve target: ' + target)
|
||||
}
|
||||
targetDefaults = targetDefaults.default || targetDefaults
|
||||
|
||||
const _defaults = defu(defaults, { target })
|
||||
const _targetInput = defu(nuxtOptions.serverless, _defaults)
|
||||
const _target = extendTarget(nuxtOptions.serverless, targetDefaults)(_targetInput)
|
||||
const options: SLSOptions = defu(nuxtOptions.serverless, _target, _defaults)
|
||||
|
||||
options.slsDir = resolvePath(options, options.slsDir)
|
||||
options.targetDir = resolvePath(options, options.targetDir)
|
||||
options.publicDir = resolvePath(options, options.publicDir)
|
||||
|
||||
return options
|
||||
}
|
110
packages/nitro/src/context.ts
Normal file
110
packages/nitro/src/context.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import { resolve } from 'upath'
|
||||
import defu from 'defu'
|
||||
import type { NuxtOptions } from '@nuxt/types'
|
||||
import Hookable, { configHooksT } from 'hookable'
|
||||
import { tryImport, resolvePath, detectTarget, extendPreset } from './utils'
|
||||
import * as PRESETS from './presets'
|
||||
|
||||
export interface ServerMiddleware {
|
||||
route: string
|
||||
handle: string
|
||||
lazy?: boolean
|
||||
}
|
||||
|
||||
export interface SigmaContext {
|
||||
timing: boolean
|
||||
inlineChunks: boolean
|
||||
minify: boolean
|
||||
externals: boolean
|
||||
analyze: boolean
|
||||
entry: string
|
||||
node: boolean
|
||||
preset: string
|
||||
rollupConfig?: any
|
||||
renderer: string
|
||||
middleware: ServerMiddleware[]
|
||||
hooks: configHooksT
|
||||
ignore: string[]
|
||||
output: {
|
||||
dir: string
|
||||
serverDir: string
|
||||
publicDir: string
|
||||
}
|
||||
_nuxt: {
|
||||
dev: boolean
|
||||
rootDir: string
|
||||
buildDir: string
|
||||
staticDir: string
|
||||
routerBase: string
|
||||
publicPath: string
|
||||
fullStatic: boolean
|
||||
staticAssets: any
|
||||
}
|
||||
_internal: {
|
||||
runtimeDir: string
|
||||
hooks: Hookable
|
||||
}
|
||||
}
|
||||
|
||||
export interface SigmaInput extends Partial<SigmaContext> {}
|
||||
|
||||
export type SigmaPreset = SigmaInput | ((input: SigmaInput) => SigmaInput)
|
||||
|
||||
export function getsigmaContext (nuxtOptions: NuxtOptions, input: SigmaInput): SigmaContext {
|
||||
const defaults: SigmaContext = {
|
||||
timing: true,
|
||||
inlineChunks: true,
|
||||
minify: true,
|
||||
externals: false,
|
||||
analyze: false,
|
||||
entry: undefined,
|
||||
node: undefined,
|
||||
preset: undefined,
|
||||
rollupConfig: undefined,
|
||||
renderer: undefined,
|
||||
middleware: [],
|
||||
ignore: [],
|
||||
hooks: {},
|
||||
output: {
|
||||
dir: '{{ _nuxt.rootDir }}/.output',
|
||||
serverDir: '{{ output.dir }}/server',
|
||||
publicDir: '{{ output.dir }}/public'
|
||||
},
|
||||
_nuxt: {
|
||||
dev: nuxtOptions.dev,
|
||||
rootDir: nuxtOptions.rootDir,
|
||||
buildDir: nuxtOptions.buildDir,
|
||||
staticDir: nuxtOptions.dir.static,
|
||||
routerBase: nuxtOptions.router.base,
|
||||
publicPath: nuxtOptions.build.publicPath,
|
||||
fullStatic: nuxtOptions.preset === 'static' && !nuxtOptions._legacyGenerate,
|
||||
// @ts-ignore
|
||||
staticAssets: nuxtOptions.generate.staticAssets
|
||||
},
|
||||
_internal: {
|
||||
runtimeDir: resolve(__dirname, '../runtime'),
|
||||
hooks: undefined
|
||||
}
|
||||
}
|
||||
|
||||
defaults.preset = input.preset || process.env.SIGMA_PRESET || detectTarget() || 'server'
|
||||
let presetDefaults = PRESETS[defaults.preset] || tryImport(nuxtOptions.rootDir, defaults.preset)
|
||||
if (!presetDefaults) {
|
||||
throw new Error('Cannot resolve preset: ' + defaults.preset)
|
||||
}
|
||||
presetDefaults = presetDefaults.default || presetDefaults
|
||||
|
||||
const _presetInput = defu(input, defaults)
|
||||
// @ts-ignore
|
||||
const _preset = extendPreset(input, presetDefaults)(_presetInput)
|
||||
const sigmaContext: SigmaContext = defu(input, _preset, defaults) as any
|
||||
|
||||
sigmaContext.output.dir = resolvePath(sigmaContext, sigmaContext.output.dir)
|
||||
sigmaContext.output.publicDir = resolvePath(sigmaContext, sigmaContext.output.publicDir)
|
||||
sigmaContext.output.serverDir = resolvePath(sigmaContext, sigmaContext.output.serverDir)
|
||||
|
||||
// console.log(sigmaContext)
|
||||
// process.exit(1)
|
||||
|
||||
return sigmaContext
|
||||
}
|
@ -1,85 +1,6 @@
|
||||
import type { Module } from '@nuxt/types'
|
||||
import { build } from './build'
|
||||
import { getoptions } from './config'
|
||||
import nuxt2 from './module/nuxt2'
|
||||
|
||||
export default <Module> function slsModule () {
|
||||
export default function () {
|
||||
const { nuxt } = this
|
||||
|
||||
if (nuxt.options.dev) {
|
||||
return
|
||||
}
|
||||
|
||||
// Config
|
||||
const options = getoptions(nuxt.options, nuxt.options.serverless || {})
|
||||
|
||||
// Tune webpack config
|
||||
nuxt.options.build._minifyServer = false
|
||||
nuxt.options.build.standalone = false
|
||||
|
||||
// Tune generator
|
||||
nuxt.options.generate.crawler = false
|
||||
if (Array.isArray(nuxt.options.generate.routes)) {
|
||||
nuxt.options.generate.routes = Array.from(new Set([
|
||||
...nuxt.options.generate.routes,
|
||||
...options.static
|
||||
]))
|
||||
}
|
||||
nuxt.options.generate.dir = options.publicDir
|
||||
|
||||
// serverMiddleware
|
||||
// TODO: render:setupMiddleware hook
|
||||
// TODO: support m.prefix and m.route
|
||||
nuxt.hook('modules:done', () => {
|
||||
const unsupported = []
|
||||
for (let m of nuxt.options.serverMiddleware) {
|
||||
if (typeof m === 'string') {
|
||||
m = { handler: m }
|
||||
}
|
||||
|
||||
const route = m.path || m.route || '/'
|
||||
const handle = nuxt.resolver.resolvePath(m.handler || m.handle)
|
||||
|
||||
if (typeof handle !== 'string' || typeof route !== 'string') {
|
||||
unsupported.push(m)
|
||||
continue
|
||||
}
|
||||
|
||||
options.serverMiddleware.push({ ...m, route, handle })
|
||||
}
|
||||
if (unsupported.length) {
|
||||
console.warn('[serverless] Unsupported Server middleware used: ', unsupported)
|
||||
console.info('Supported format is `{ path: string, handler: string }` and handler should export `(req, res) => {}`')
|
||||
}
|
||||
})
|
||||
|
||||
if (options.nuxtHooks) {
|
||||
nuxt.addHooks(options.nuxtHooks)
|
||||
}
|
||||
|
||||
nuxt.hook('generate:cache:ignore', (ignore: string[]) => {
|
||||
ignore.push(options.slsDir)
|
||||
ignore.push(options.targetDir)
|
||||
ignore.push(...options.generateIgnore)
|
||||
})
|
||||
|
||||
nuxt.hook('generate:page', (page) => {
|
||||
// TODO: Use ssrContext
|
||||
if (!options.static.includes(page.route)) {
|
||||
page.exclude = true
|
||||
}
|
||||
})
|
||||
|
||||
nuxt.hook('generate:before', async () => {
|
||||
console.info('Building light version for `nuxt generate`')
|
||||
const { entry } = await build(getoptions(nuxt.options, {
|
||||
target: 'cjs',
|
||||
serverMiddleware: options.serverMiddleware
|
||||
}))
|
||||
console.info('Loading lambda')
|
||||
require(entry)
|
||||
})
|
||||
|
||||
nuxt.hook('generate:done', async () => {
|
||||
await build(options)
|
||||
})
|
||||
return nuxt2(nuxt)
|
||||
}
|
||||
|
126
packages/nitro/src/module/nuxt2.ts
Normal file
126
packages/nitro/src/module/nuxt2.ts
Normal file
@ -0,0 +1,126 @@
|
||||
import fetch from 'node-fetch'
|
||||
import { resolve } from 'upath'
|
||||
import { build } from '../build'
|
||||
import { getsigmaContext, SigmaContext } from '../context'
|
||||
import { createDevServer } from '../server'
|
||||
import wpfs from '../utils/wpfs'
|
||||
|
||||
export default function (nuxt) {
|
||||
// Build in node_modules/.cache/nuxt
|
||||
const oldBuildDir = nuxt.options.buildDir
|
||||
nuxt.options.buildDir = resolve(nuxt.options.rootDir, 'node_modules/.cache/nuxt')
|
||||
nuxt.options.build.transpile = nuxt.options.build.transpile || []
|
||||
nuxt.options.build.transpile.push(nuxt.options.buildDir)
|
||||
nuxt.options.appTemplatePath = nuxt.options.appTemplatePath
|
||||
.replace(oldBuildDir, nuxt.options.buildDir)
|
||||
|
||||
// Create contexts
|
||||
const sigmaContext = getsigmaContext(nuxt.options, nuxt.options.sigma || {})
|
||||
const sigmaDevContext = getsigmaContext(nuxt.options, { preset: 'dev' })
|
||||
|
||||
// Use nuxt as main hooks host
|
||||
sigmaContext._internal.hooks = nuxt
|
||||
sigmaDevContext._internal.hooks = nuxt
|
||||
nuxt.addHooks(sigmaContext.hooks)
|
||||
|
||||
// Replace nuxt server
|
||||
if (nuxt.server) {
|
||||
nuxt.server.__closed = true
|
||||
nuxt.server = createNuxt2DevServer(sigmaDevContext)
|
||||
nuxt.addHooks(sigmaDevContext.hooks)
|
||||
}
|
||||
|
||||
// serverMiddleware bridge
|
||||
// TODO: render:setupMiddleware hook
|
||||
// TODO: support m.prefix and m.route
|
||||
nuxt.hook('modules:done', () => {
|
||||
const unsupported = []
|
||||
for (let m of nuxt.options.serverMiddleware) {
|
||||
if (typeof m === 'string') { m = { handler: m } }
|
||||
const route = m.path || m.route || '/'
|
||||
let handle = m.handler || m.handle
|
||||
if (typeof handle !== 'string' || typeof route !== 'string') {
|
||||
if (route === '/_loading') {
|
||||
nuxt.server.setLoadingMiddleware(handle)
|
||||
continue
|
||||
}
|
||||
unsupported.push(m)
|
||||
continue
|
||||
}
|
||||
handle = nuxt.resolver.resolvePath(handle)
|
||||
sigmaContext.middleware.push({ ...m, route, handle })
|
||||
sigmaDevContext.middleware.push({ ...m, route, handle })
|
||||
}
|
||||
nuxt.options.serverMiddleware = [...unsupported]
|
||||
if (unsupported.length) {
|
||||
console.warn('[sigma] Unsupported Server middleware used: \n', ...unsupported)
|
||||
console.info('Supported format is `{ path: string, handler: string }` and handler should export `(req, res) => {}`')
|
||||
}
|
||||
})
|
||||
|
||||
// nuxt build/dev
|
||||
nuxt.options.build._minifyServer = false
|
||||
nuxt.options.build.standalone = false
|
||||
nuxt.hook('build:done', async () => {
|
||||
await build(nuxt.options.dev ? sigmaDevContext : sigmaContext)
|
||||
})
|
||||
|
||||
// nude dev
|
||||
if (nuxt.options.dev) {
|
||||
nuxt.hook('sigma:compiled', () => { nuxt.server.watch() })
|
||||
nuxt.hook('build:compile', ({ compiler }) => { compiler.outputFileSystem = wpfs })
|
||||
nuxt.hook('server:devMiddleware', (m) => { nuxt.server.setDevMiddleware(m) })
|
||||
}
|
||||
|
||||
// nuxt generate
|
||||
nuxt.hook('generate:cache:ignore', (ignore: string[]) => {
|
||||
ignore.push(sigmaContext.output.dir)
|
||||
ignore.push(sigmaContext.output.serverDir)
|
||||
ignore.push(sigmaContext.output.publicDir)
|
||||
ignore.push(...sigmaContext.ignore)
|
||||
})
|
||||
|
||||
// generate:bfore is before webpack build that we need!
|
||||
nuxt.hook('generate:extendRoutes', async () => {
|
||||
await build(sigmaDevContext)
|
||||
await nuxt.server.reload()
|
||||
})
|
||||
}
|
||||
|
||||
function createNuxt2DevServer (sigmaContext: SigmaContext) {
|
||||
const server = createDevServer(sigmaContext)
|
||||
|
||||
const listeners = []
|
||||
async function listen (port) {
|
||||
const listener = await server.listen(port)
|
||||
listeners.push(listener)
|
||||
return listeners
|
||||
}
|
||||
|
||||
async function renderRoute (route = '/', renderContext = {}) {
|
||||
const [listener] = listeners
|
||||
if (!listener) {
|
||||
throw new Error('There is no server listener to call `server.renderRoute()`')
|
||||
}
|
||||
const html = await fetch(listener.url + route, {
|
||||
headers: { 'nuxt-render-context': encodeQuery(renderContext) }
|
||||
}).then(r => r.text())
|
||||
|
||||
return { html }
|
||||
}
|
||||
|
||||
return {
|
||||
...server,
|
||||
listeners,
|
||||
renderRoute,
|
||||
listen,
|
||||
serverMiddlewarePaths () { return [] },
|
||||
ready () {}
|
||||
}
|
||||
}
|
||||
|
||||
function encodeQuery (obj) {
|
||||
return Object.entries(obj).map(
|
||||
([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`
|
||||
).join('&')
|
||||
}
|
40
packages/nitro/src/presets/browser.ts
Normal file
40
packages/nitro/src/presets/browser.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { writeFile } from 'fs-extra'
|
||||
import { resolve } from 'upath'
|
||||
import consola from 'consola'
|
||||
import { extendPreset, prettyPath } from '../utils'
|
||||
import { SigmaPreset, SigmaContext, SigmaInput } from '../context'
|
||||
import { worker } from './worker'
|
||||
|
||||
export const browser: SigmaPreset = extendPreset(worker, (input: SigmaInput) => {
|
||||
const script = `<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('${input._nuxt.routerBase}index.js');
|
||||
});
|
||||
}
|
||||
</script>`
|
||||
|
||||
return <SigmaInput> {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/service-worker',
|
||||
output: {
|
||||
dir: '{{ _nuxt.rootDir }}/.output/public',
|
||||
publicDir: '{{ output.dir }}',
|
||||
serverDir: '{{ output.dir }}'
|
||||
},
|
||||
hooks: {
|
||||
'vue-renderer:ssr:templateParams' (params) {
|
||||
params.APP += script
|
||||
},
|
||||
'vue-renderer:spa:templateParams' (params) {
|
||||
params.APP += script
|
||||
},
|
||||
'sigma:template:document' (tmpl) {
|
||||
tmpl.compiled = tmpl.compiled.replace('</body>', script + '</body>')
|
||||
},
|
||||
async 'sigma:compiled' ({ output }: SigmaContext) {
|
||||
await writeFile(resolve(output.publicDir, 'index.html'), script) // TODO
|
||||
consola.info('Ready to deploy to static hosting:', prettyPath(output.publicDir))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
15
packages/nitro/src/presets/cli.ts
Normal file
15
packages/nitro/src/presets/cli.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import consola from 'consola'
|
||||
import { extendPreset, prettyPath } from '../utils'
|
||||
import { SigmaPreset, SigmaContext } from '../context'
|
||||
import { node } from './node'
|
||||
|
||||
export const cli: SigmaPreset = extendPreset(node, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/cli',
|
||||
externals: true,
|
||||
inlineChunks: true,
|
||||
hooks: {
|
||||
'sigma:compiled' ({ output }: SigmaContext) {
|
||||
consola.info('Run with `node ' + prettyPath(output.serverDir) + ' [route]`')
|
||||
}
|
||||
}
|
||||
})
|
19
packages/nitro/src/presets/cloudflare.ts
Normal file
19
packages/nitro/src/presets/cloudflare.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { resolve } from 'upath'
|
||||
import consola from 'consola'
|
||||
import { extendPreset, writeFile, prettyPath } from '../utils'
|
||||
import { SigmaContext, SigmaPreset } from '../context'
|
||||
import { worker } from './worker'
|
||||
|
||||
export const cloudflare: SigmaPreset = extendPreset(worker, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/cloudflare',
|
||||
ignore: [
|
||||
'wrangler.toml'
|
||||
],
|
||||
hooks: {
|
||||
async 'sigma:compiled' ({ output, _nuxt }: SigmaContext) {
|
||||
await writeFile(resolve(output.dir, 'package.json'), JSON.stringify({ private: true, main: './server/index.js' }, null, 2))
|
||||
await writeFile(resolve(output.dir, 'package-lock.json'), JSON.stringify({ lockfileVersion: 1 }, null, 2))
|
||||
consola.success('Ready to run `wrangler publish` in', prettyPath(_nuxt.rootDir))
|
||||
}
|
||||
}
|
||||
})
|
11
packages/nitro/src/presets/dev.ts
Normal file
11
packages/nitro/src/presets/dev.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { extendPreset } from '../utils'
|
||||
import { SigmaPreset } from '../context'
|
||||
import { node } from './node'
|
||||
|
||||
export const dev: SigmaPreset = extendPreset(node, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/dev',
|
||||
minify: false,
|
||||
externals: true,
|
||||
inlineChunks: true,
|
||||
timing: true
|
||||
})
|
@ -3,6 +3,8 @@ export * from './cloudflare'
|
||||
export * from './lambda'
|
||||
export * from './netlify'
|
||||
export * from './node'
|
||||
export * from './cjs'
|
||||
export * from './dev'
|
||||
export * from './server'
|
||||
export * from './cli'
|
||||
export * from './vercel'
|
||||
export * from './worker'
|
7
packages/nitro/src/presets/lambda.ts
Normal file
7
packages/nitro/src/presets/lambda.ts
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
import { SigmaPreset } from '../context'
|
||||
|
||||
export const lambda: SigmaPreset = {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/lambda',
|
||||
inlineChunks: false
|
||||
}
|
10
packages/nitro/src/presets/netlify.ts
Normal file
10
packages/nitro/src/presets/netlify.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { extendPreset } from '../utils'
|
||||
import { SigmaPreset } from '../context'
|
||||
import { lambda } from './lambda'
|
||||
|
||||
export const netlify: SigmaPreset = extendPreset(lambda, {
|
||||
ignore: [
|
||||
'netlify.toml',
|
||||
'_redirects'
|
||||
]
|
||||
})
|
6
packages/nitro/src/presets/node.ts
Normal file
6
packages/nitro/src/presets/node.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { SigmaPreset } from '../context'
|
||||
|
||||
export const node: SigmaPreset = {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/node',
|
||||
inlineChunks: false
|
||||
}
|
16
packages/nitro/src/presets/server.ts
Normal file
16
packages/nitro/src/presets/server.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import consola from 'consola'
|
||||
import { extendPreset, hl, prettyPath } from '../utils'
|
||||
import { SigmaPreset, SigmaContext } from '../context'
|
||||
import { node } from './node'
|
||||
|
||||
export const server: SigmaPreset = extendPreset(node, {
|
||||
entry: '{{ _internal.runtimeDir }}/entries/server',
|
||||
externals: false,
|
||||
inlineChunks: false,
|
||||
timing: true,
|
||||
hooks: {
|
||||
'sigma:compiled' ({ output }: SigmaContext) {
|
||||
consola.success('Ready to run', hl('node ' + prettyPath(output.serverDir)))
|
||||
}
|
||||
}
|
||||
})
|
48
packages/nitro/src/presets/vercel.ts
Normal file
48
packages/nitro/src/presets/vercel.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { resolve } from 'upath'
|
||||
import { extendPreset, writeFile } from '../utils'
|
||||
import { SigmaPreset, SigmaContext } from '../context'
|
||||
import { node } from './node'
|
||||
|
||||
export const vercel: SigmaPreset = extendPreset(node, {
|
||||
output: {
|
||||
dir: '{{ _nuxt.rootDir }}/.vercel_build_output',
|
||||
serverDir: '{{ output.dir }}/functions/node/_nuxt/index.js',
|
||||
publicDir: '{{ output.dir }}/static'
|
||||
},
|
||||
ignore: [
|
||||
'vercel.json'
|
||||
],
|
||||
hooks: {
|
||||
async 'sigma:compiled' (ctx: SigmaContext) {
|
||||
await writeRoutes(ctx)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function writeRoutes ({ output }: SigmaContext) {
|
||||
const routes = [
|
||||
{
|
||||
src: '/sw.js',
|
||||
headers: {
|
||||
'cache-control': 'public, max-age=0, must-revalidate'
|
||||
},
|
||||
continue: true
|
||||
},
|
||||
{
|
||||
src: '/_nuxt/(.*)',
|
||||
headers: {
|
||||
'cache-control': 'public,max-age=31536000,immutable'
|
||||
},
|
||||
continue: true
|
||||
},
|
||||
{
|
||||
handle: 'filesystem'
|
||||
},
|
||||
{
|
||||
src: '(.*)',
|
||||
dest: '/.vercel/functions/_nuxt/index'
|
||||
}
|
||||
]
|
||||
|
||||
await writeFile(resolve(output.dir, 'config/routes.json'), JSON.stringify(routes, null, 2))
|
||||
}
|
11
packages/nitro/src/presets/worker.ts
Normal file
11
packages/nitro/src/presets/worker.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { SigmaPreset, SigmaContext } from '../context'
|
||||
|
||||
export const worker: SigmaPreset = {
|
||||
entry: null, // Abstract
|
||||
node: false,
|
||||
hooks: {
|
||||
'sigma:rollup:before' ({ rollupConfig }: SigmaContext) {
|
||||
rollupConfig.output.format = 'iife'
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import Module from 'module'
|
||||
import { dirname, join, relative, resolve } from 'path'
|
||||
import { dirname, join, relative, resolve } from 'upath'
|
||||
import { InputOptions, OutputOptions } from 'rollup'
|
||||
import { terser } from 'rollup-plugin-terser'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
@ -10,110 +10,64 @@ import replace from '@rollup/plugin-replace'
|
||||
import virtual from '@rollup/plugin-virtual'
|
||||
import inject from '@rollup/plugin-inject'
|
||||
import analyze from 'rollup-plugin-analyzer'
|
||||
import * as un from '@nuxt/un'
|
||||
|
||||
import hasha from 'hasha'
|
||||
import { SLSOptions } from '../config'
|
||||
import { SigmaContext } from '../context'
|
||||
import { resolvePath, MODULE_DIR } from '../utils'
|
||||
|
||||
import { dynamicRequire } from './dynamic-require'
|
||||
import { externals } from './externals'
|
||||
import { timing } from './timing'
|
||||
|
||||
const mapArrToVal = (val, arr) => arr.reduce((p, c) => ({ ...p, [c]: val }), {})
|
||||
|
||||
export type RollupConfig = InputOptions & { output: OutputOptions }
|
||||
|
||||
export const getRollupConfig = (options: SLSOptions) => {
|
||||
const extensions: string[] = ['.ts', '.mjs', '.js', '.json', '.node']
|
||||
export const getRollupConfig = (sigmaContext: SigmaContext) => {
|
||||
const extensions: string[] = ['.ts', '.js', '.json', '.node']
|
||||
|
||||
const external: InputOptions['external'] = []
|
||||
|
||||
const injects:{ [key: string]: string| string[] } = {}
|
||||
const presets = []
|
||||
|
||||
const aliases: { [key: string]: string } = {}
|
||||
|
||||
Object.assign(aliases, mapArrToVal('~mocks/generic', [
|
||||
// @nuxt/devalue
|
||||
'consola',
|
||||
// vue2
|
||||
'encoding',
|
||||
'stream',
|
||||
'he',
|
||||
'resolve',
|
||||
'source-map',
|
||||
'lodash.template',
|
||||
'serialize-javascript',
|
||||
// vue3
|
||||
'@babel/parser',
|
||||
'@vue/compiler-core',
|
||||
'@vue/compiler-dom',
|
||||
'@vue/compiler-ssr'
|
||||
]))
|
||||
|
||||
// Uses eval 😈
|
||||
aliases.depd = '~mocks/custom/depd'
|
||||
|
||||
if (options.node === false) {
|
||||
// Globals
|
||||
// injects.Buffer = ['buffer', 'Buffer'] <-- TODO: Make it opt-in
|
||||
injects.process = '~mocks/node/process'
|
||||
|
||||
// Aliases
|
||||
Object.assign(aliases, {
|
||||
// Node
|
||||
...mapArrToVal('~mocks/generic', Module.builtinModules),
|
||||
http: '~mocks/node/http',
|
||||
fs: '~mocks/node/fs',
|
||||
process: '~mocks/node/process',
|
||||
'node-process': require.resolve('process/browser.js'),
|
||||
// buffer: require.resolve('buffer/index.js'),
|
||||
util: require.resolve('util/util.js'),
|
||||
events: require.resolve('events/events.js'),
|
||||
inherits: require.resolve('inherits/inherits_browser.js'),
|
||||
|
||||
// Custom
|
||||
'node-fetch': '~mocks/custom/node-fetch',
|
||||
etag: '~mocks/generic/noop',
|
||||
|
||||
// Express
|
||||
...mapArrToVal('~mocks/generic', [
|
||||
'serve-static',
|
||||
'iconv-lite'
|
||||
]),
|
||||
|
||||
// Mime
|
||||
'mime-db': '~mocks/custom/mime-db',
|
||||
'mime/lite': require.resolve('mime/lite'),
|
||||
mime: '~mocks/custom/mime'
|
||||
})
|
||||
if (sigmaContext.node === false) {
|
||||
presets.push(un.nodeless)
|
||||
} else {
|
||||
presets.push(un.node)
|
||||
external.push(...Module.builtinModules)
|
||||
}
|
||||
|
||||
const chunksDirName = join(dirname(options.outName), 'chunks')
|
||||
const serverDir = join(options.buildDir, 'dist/server')
|
||||
const env = un.env(...presets, {
|
||||
alias: {
|
||||
depd: require.resolve('@nuxt/un/runtime/npm/depd')
|
||||
}
|
||||
})
|
||||
|
||||
const buildServerDir = join(sigmaContext._nuxt.buildDir, 'dist/server')
|
||||
const runtimeAppDir = join(sigmaContext._internal.runtimeDir, 'app')
|
||||
|
||||
const rollupConfig: RollupConfig = {
|
||||
input: resolvePath(options, options.entry),
|
||||
input: resolvePath(sigmaContext, sigmaContext.entry),
|
||||
output: {
|
||||
dir: options.targetDir,
|
||||
entryFileNames: options.outName,
|
||||
dir: sigmaContext.output.serverDir,
|
||||
entryFileNames: 'index.js',
|
||||
chunkFileNames (chunkInfo) {
|
||||
let prefix = ''
|
||||
const modules = Object.keys(chunkInfo.modules)
|
||||
const lastModule = modules[modules.length - 1]
|
||||
if (lastModule.startsWith(serverDir)) {
|
||||
prefix = join('ssr', relative(serverDir, dirname(lastModule)))
|
||||
} else if (lastModule.startsWith(options.buildDir)) {
|
||||
prefix = 'ssr'
|
||||
} else if (lastModule.startsWith(options.runtimeDir)) {
|
||||
prefix = 'runtime'
|
||||
} else if (!prefix && options.serverMiddleware.find(m => lastModule.startsWith(m.handle))) {
|
||||
if (lastModule.startsWith(buildServerDir)) {
|
||||
prefix = join('app', relative(buildServerDir, dirname(lastModule)))
|
||||
} else if (lastModule.startsWith(runtimeAppDir)) {
|
||||
prefix = 'app'
|
||||
} else if (lastModule.startsWith(sigmaContext._nuxt.buildDir)) {
|
||||
prefix = 'nuxt'
|
||||
} else if (lastModule.startsWith(sigmaContext._internal.runtimeDir)) {
|
||||
prefix = 'sigma'
|
||||
} else if (!prefix && sigmaContext.middleware.find(m => lastModule.startsWith(m.handle))) {
|
||||
prefix = 'middleware'
|
||||
}
|
||||
return join(chunksDirName, prefix, '[name].js')
|
||||
return join('chunks', prefix, '[name].js')
|
||||
},
|
||||
inlineDynamicImports: options.inlineChunks,
|
||||
inlineDynamicImports: sigmaContext.inlineChunks,
|
||||
format: 'cjs',
|
||||
exports: 'auto',
|
||||
intro: '',
|
||||
@ -124,28 +78,28 @@ export const getRollupConfig = (options: SLSOptions) => {
|
||||
plugins: []
|
||||
}
|
||||
|
||||
if (options.timing) {
|
||||
if (sigmaContext.timing) {
|
||||
rollupConfig.plugins.push(timing())
|
||||
}
|
||||
|
||||
// https://github.com/rollup/plugins/tree/master/packages/replace
|
||||
rollupConfig.plugins.push(replace({
|
||||
values: {
|
||||
'process.env.NODE_ENV': '"production"',
|
||||
'process.env.NODE_ENV': sigmaContext._nuxt.dev ? '"development"' : '"production"',
|
||||
'typeof window': '"undefined"',
|
||||
'process.env.ROUTER_BASE': JSON.stringify(options.routerBase),
|
||||
'process.env.PUBLIC_PATH': JSON.stringify(options.publicPath),
|
||||
'process.env.NUXT_STATIC_BASE': JSON.stringify(options.staticAssets.base),
|
||||
'process.env.NUXT_STATIC_VERSION': JSON.stringify(options.staticAssets.version),
|
||||
'process.env.ROUTER_BASE': JSON.stringify(sigmaContext._nuxt.routerBase),
|
||||
'process.env.PUBLIC_PATH': JSON.stringify(sigmaContext._nuxt.publicPath),
|
||||
'process.env.NUXT_STATIC_BASE': JSON.stringify(sigmaContext._nuxt.staticAssets.base),
|
||||
'process.env.NUXT_STATIC_VERSION': JSON.stringify(sigmaContext._nuxt.staticAssets.version),
|
||||
// @ts-ignore
|
||||
'process.env.NUXT_FULL_STATIC': options.fullStatic
|
||||
'process.env.NUXT_FULL_STATIC': sigmaContext.fullStatic
|
||||
}
|
||||
}))
|
||||
|
||||
// Dynamic Require Support
|
||||
rollupConfig.plugins.push(dynamicRequire({
|
||||
dir: resolve(options.buildDir, 'dist/server'),
|
||||
inline: options.node === false || options.inlineChunks,
|
||||
dir: resolve(sigmaContext._nuxt.buildDir, 'dist/server'),
|
||||
inline: sigmaContext.node === false || sigmaContext.inlineChunks,
|
||||
globbyOptions: {
|
||||
ignore: [
|
||||
'server.js'
|
||||
@ -166,36 +120,39 @@ export const getRollupConfig = (options: SLSOptions) => {
|
||||
const getImportId = p => '_' + hasha(p).substr(0, 6)
|
||||
rollupConfig.plugins.push(virtual({
|
||||
'~serverMiddleware': `
|
||||
${options.serverMiddleware.filter(m => !m.lazy).map(m => `import ${getImportId(m.handle)} from '${m.handle}';`).join('\n')}
|
||||
${sigmaContext.middleware.filter(m => !m.lazy).map(m => `import ${getImportId(m.handle)} from '${m.handle}';`).join('\n')}
|
||||
|
||||
${options.serverMiddleware.filter(m => m.lazy).map(m => `const ${getImportId(m.handle)} = () => import('${m.handle}');`).join('\n')}
|
||||
${sigmaContext.middleware.filter(m => m.lazy).map(m => `const ${getImportId(m.handle)} = () => import('${m.handle}');`).join('\n')}
|
||||
|
||||
export default [
|
||||
${options.serverMiddleware.map(m => `{ route: '${m.route}', handle: ${getImportId(m.handle)}, lazy: ${m.lazy || false} }`).join(',\n')}
|
||||
${sigmaContext.middleware.map(m => `{ route: '${m.route}', handle: ${getImportId(m.handle)}, lazy: ${m.lazy || false} }`).join(',\n')}
|
||||
];
|
||||
`
|
||||
}))
|
||||
|
||||
// Polyfill
|
||||
rollupConfig.plugins.push(virtual({
|
||||
'~polyfill': env.polyfill.map(p => `require('${p}');`).join('\n')
|
||||
}))
|
||||
|
||||
// https://github.com/rollup/plugins/tree/master/packages/alias
|
||||
const renderer = options.renderer || 'vue2'
|
||||
const renderer = sigmaContext.renderer || 'vue2'
|
||||
rollupConfig.plugins.push(alias({
|
||||
entries: {
|
||||
'~runtime': options.runtimeDir,
|
||||
'~mocks': resolve(options.runtimeDir, 'mocks'),
|
||||
'~renderer': require.resolve(resolve(options.runtimeDir, 'ssr', renderer)),
|
||||
'~build': options.buildDir,
|
||||
'~mock': require.resolve(resolve(options.runtimeDir, 'mocks/generic')),
|
||||
...aliases
|
||||
'~runtime': sigmaContext._internal.runtimeDir,
|
||||
'~renderer': require.resolve(resolve(sigmaContext._internal.runtimeDir, 'app', renderer)),
|
||||
'~build': sigmaContext._nuxt.buildDir,
|
||||
...env.alias
|
||||
}
|
||||
}))
|
||||
|
||||
// External Plugin
|
||||
if (options.externals) {
|
||||
if (sigmaContext.externals) {
|
||||
rollupConfig.plugins.push(externals({
|
||||
relativeTo: options.targetDir,
|
||||
relativeTo: sigmaContext.output.serverDir,
|
||||
include: [
|
||||
options.runtimeDir,
|
||||
...options.serverMiddleware.map(m => m.handle)
|
||||
sigmaContext._internal.runtimeDir,
|
||||
...sigmaContext.middleware.map(m => m.handle)
|
||||
]
|
||||
}))
|
||||
}
|
||||
@ -204,12 +161,12 @@ export const getRollupConfig = (options: SLSOptions) => {
|
||||
rollupConfig.plugins.push(nodeResolve({
|
||||
extensions,
|
||||
preferBuiltins: true,
|
||||
rootDir: options.rootDir,
|
||||
rootDir: sigmaContext._nuxt.rootDir,
|
||||
// https://www.npmjs.com/package/resolve
|
||||
customResolveOptions: {
|
||||
basedir: options.rootDir,
|
||||
basedir: sigmaContext._nuxt.rootDir,
|
||||
paths: [
|
||||
resolve(options.rootDir, 'node_modukes'),
|
||||
resolve(sigmaContext._nuxt.rootDir, 'node_modules'),
|
||||
resolve(MODULE_DIR, 'node_modules')
|
||||
]
|
||||
},
|
||||
@ -225,16 +182,16 @@ export const getRollupConfig = (options: SLSOptions) => {
|
||||
rollupConfig.plugins.push(json())
|
||||
|
||||
// https://github.com/rollup/plugins/tree/master/packages/inject
|
||||
rollupConfig.plugins.push(inject(injects))
|
||||
rollupConfig.plugins.push(inject(env.inject))
|
||||
|
||||
if (options.analyze) {
|
||||
if (sigmaContext.analyze) {
|
||||
// https://github.com/doesdev/rollup-plugin-analyzer
|
||||
rollupConfig.plugins.push(analyze())
|
||||
}
|
||||
|
||||
// https://github.com/TrySound/rollup-plugin-terser
|
||||
// https://github.com/terser/terser#minify-options
|
||||
if (options.minify !== false) {
|
||||
// https://github.com/terser/terser#minify-sigmaContext
|
||||
if (sigmaContext.minify) {
|
||||
rollupConfig.plugins.push(terser({
|
||||
mangle: {
|
||||
keep_fnames: true,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { resolve } from 'path'
|
||||
import { resolve } from 'upath'
|
||||
import globby, { GlobbyOptions } from 'globby'
|
||||
import type { Plugin } from 'rollup'
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { isAbsolute, relative } from 'path'
|
||||
import { isAbsolute, relative } from 'upath'
|
||||
|
||||
export function externals ({ include = [], relativeTo }) {
|
||||
return {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { extname } from 'path'
|
||||
import { extname } from 'upath'
|
||||
import type { Plugin, RenderedChunk } from 'rollup'
|
||||
|
||||
export interface Options { }
|
||||
@ -20,7 +20,7 @@ const end = s => { const d = hrtime(s); return ((d[0] * 1e9) + d[1]) / 1e6; };
|
||||
const _s = {};
|
||||
const metrics = [];
|
||||
const logStart = id => { _s[id] = hrtime(); };
|
||||
const logEnd = id => { const t = end(_s[id]); delete _s[id]; metrics.push([id, t]); console.log('◈', id, t, 'ms'); };
|
||||
const logEnd = id => { const t = end(_s[id]); delete _s[id]; metrics.push([id, t]); console.debug('>', id + ' (' + t + 'ms)'); };
|
||||
${TIMING} = { hrtime, start, end, metrics, logStart, logEnd };
|
||||
`)
|
||||
|
||||
@ -30,7 +30,8 @@ export function timing (_opts: Options = {}): Plugin {
|
||||
renderChunk (code, chunk: RenderedChunk) {
|
||||
let name = chunk.fileName || ''
|
||||
name = name.replace(extname(name), '')
|
||||
return "'use strict';" + (chunk.isEntry ? HELPER : '') + `${TIMING}.logStart('import:${name}');` + code + `;${TIMING}.logEnd('import:${name}');`
|
||||
const logName = name === 'index' ? 'entry' : name
|
||||
return "'use strict';" + (chunk.isEntry ? HELPER : '') + `${TIMING}.logStart('${logName}');` + code + `;${TIMING}.logEnd('${logName}');`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
137
packages/nitro/src/server.ts
Normal file
137
packages/nitro/src/server.ts
Normal file
@ -0,0 +1,137 @@
|
||||
import { Worker } from 'worker_threads'
|
||||
import { Server } from 'http'
|
||||
import { resolve } from 'upath'
|
||||
import debounce from 'debounce'
|
||||
import connect from 'connect'
|
||||
import getPort from 'get-port-please'
|
||||
import chokidar from 'chokidar'
|
||||
import serveStatic from 'serve-static'
|
||||
import { createProxy } from 'http-proxy'
|
||||
import { stat } from 'fs-extra'
|
||||
import type { SigmaContext } from './context'
|
||||
|
||||
export function createDevServer (sigmaContext: SigmaContext) {
|
||||
// Worker
|
||||
const workerEntry = resolve(sigmaContext.output.dir, sigmaContext.output.serverDir, 'index.js')
|
||||
let pendingWorker: Worker
|
||||
let activeWorker: Worker
|
||||
let workerAddress: string
|
||||
async function reload () {
|
||||
if (pendingWorker) {
|
||||
await pendingWorker.terminate()
|
||||
workerAddress = null
|
||||
pendingWorker = null
|
||||
}
|
||||
if (!(await stat(workerEntry)).isFile) {
|
||||
throw new Error('Entry not found: ' + workerEntry)
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const worker = pendingWorker = new Worker(workerEntry)
|
||||
worker.once('exit', (code) => {
|
||||
if (code) {
|
||||
reject(new Error('[worker] exited with code: ' + code))
|
||||
}
|
||||
})
|
||||
worker.on('error', (err) => {
|
||||
err.message = '[worker] ' + err.message
|
||||
reject(err)
|
||||
})
|
||||
worker.on('message', (event) => {
|
||||
if (event && event.port) {
|
||||
workerAddress = 'http://localhost:' + event.port
|
||||
activeWorker = worker
|
||||
pendingWorker = null
|
||||
resolve(workerAddress)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// App
|
||||
const app = connect()
|
||||
|
||||
// _nuxt and static
|
||||
app.use(sigmaContext._nuxt.publicPath, serveStatic(resolve(sigmaContext._nuxt.buildDir, 'dist/client')))
|
||||
app.use(sigmaContext._nuxt.routerBase, serveStatic(resolve(sigmaContext._nuxt.staticDir)))
|
||||
|
||||
// Dev Middleware
|
||||
let loadingMiddleware, devMiddleware
|
||||
const setLoadingMiddleware = (m) => { loadingMiddleware = m }
|
||||
const setDevMiddleware = (m) => { devMiddleware = m }
|
||||
app.use((req, res, next) => {
|
||||
if (loadingMiddleware && req.url.startsWith('/_loading')) {
|
||||
req.url = req.url.replace('/_loading', '')
|
||||
return loadingMiddleware(req, res)
|
||||
}
|
||||
if (devMiddleware) {
|
||||
return devMiddleware(req, res, next)
|
||||
}
|
||||
return next()
|
||||
})
|
||||
|
||||
// SSR Proxy
|
||||
const proxy = createProxy()
|
||||
app.use((req, res) => {
|
||||
if (workerAddress) {
|
||||
proxy.web(req, res, { target: workerAddress })
|
||||
} else if (loadingMiddleware) {
|
||||
// TODO:serverIndex method is not exposed
|
||||
// loadingMiddleware(req, res)
|
||||
sigmaContext._internal.hooks.callHook('server:nuxt:renderLoading', req, res)
|
||||
} else {
|
||||
res.end('Worker not ready!')
|
||||
}
|
||||
})
|
||||
|
||||
// Listen
|
||||
const listeners: Server[] = []
|
||||
async function listen (port) {
|
||||
port = await getPort({ name: 'nuxt' })
|
||||
const listener = await new Promise<Server>((resolve, reject) => {
|
||||
const l = app.listen(port, err => err ? reject(err) : resolve(l))
|
||||
})
|
||||
listeners.push(listener)
|
||||
return {
|
||||
url: 'http://localhost:' + port
|
||||
}
|
||||
}
|
||||
|
||||
// Watch for dist and reload worker
|
||||
const pattern = '**/*.{js,json}'
|
||||
const events = ['add', 'change']
|
||||
let watcher
|
||||
function watch () {
|
||||
if (watcher) { return }
|
||||
const dReload = debounce(() => reload().catch(console.warn), 200, true)
|
||||
watcher = chokidar.watch([
|
||||
resolve(sigmaContext.output.serverDir, pattern),
|
||||
resolve(sigmaContext._nuxt.buildDir, 'dist/server', pattern)
|
||||
]).on('all', event => events.includes(event) && dReload())
|
||||
}
|
||||
|
||||
// Close handler
|
||||
async function close () {
|
||||
if (watcher) {
|
||||
await watcher.close()
|
||||
}
|
||||
if (activeWorker) {
|
||||
await activeWorker.terminate()
|
||||
}
|
||||
if (pendingWorker) {
|
||||
await pendingWorker.terminate()
|
||||
}
|
||||
await Promise.all(listeners.map(l => new Promise((resolve, reject) => {
|
||||
l.close(err => err ? reject(err) : resolve())
|
||||
})))
|
||||
}
|
||||
sigmaContext._internal.hooks.hook('close', close)
|
||||
|
||||
return {
|
||||
reload,
|
||||
listen,
|
||||
close,
|
||||
watch,
|
||||
setLoadingMiddleware,
|
||||
setDevMiddleware
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
import { resolve, relative } from 'path'
|
||||
import { existsSync, copy } from 'fs-extra'
|
||||
import consola from 'consola'
|
||||
import { extendTarget } from '../utils'
|
||||
import { SLSTarget } from '../config'
|
||||
import { worker } from './worker'
|
||||
|
||||
export const browser: SLSTarget = extendTarget(worker, (options) => {
|
||||
const script = `<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('${options.routerBase}_nuxt.js');
|
||||
});
|
||||
}
|
||||
</script>`.replace(/\n| +/g, '')
|
||||
|
||||
return {
|
||||
entry: '{{ runtimeDir }}/targets/service-worker',
|
||||
targetDir: '{{ publicDir }}',
|
||||
outName: '_nuxt.js',
|
||||
cleanTargetDir: false,
|
||||
nuxtHooks: {
|
||||
'vue-renderer:ssr:templateParams' (params) {
|
||||
params.APP += script
|
||||
},
|
||||
'vue-renderer:spa:templateParams' (params) {
|
||||
params.APP += script
|
||||
}
|
||||
},
|
||||
hooks: {
|
||||
'template:document' (tmpl) {
|
||||
tmpl.compiled = tmpl.compiled.replace('</body>', script + '</body>')
|
||||
},
|
||||
async done ({ rootDir, publicDir }) {
|
||||
const fallback200 = resolve(publicDir, '200.html')
|
||||
const fallback404 = resolve(publicDir, '404.html')
|
||||
if (!existsSync(fallback404) && existsSync(fallback200)) {
|
||||
await copy(fallback200, fallback404)
|
||||
}
|
||||
consola.info(`Try with \`nuxt start ${relative(process.cwd(), rootDir)}\``)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
@ -1,10 +0,0 @@
|
||||
import { extendTarget } from '../utils'
|
||||
import { SLSTarget } from '../config'
|
||||
import { node } from './node'
|
||||
|
||||
export const cjs: SLSTarget = extendTarget(node, {
|
||||
entry: '{{ runtimeDir }}/targets/cjs',
|
||||
minify: false,
|
||||
externals: true,
|
||||
inlineChunks: true
|
||||
})
|
@ -1,21 +0,0 @@
|
||||
import { resolve } from 'path'
|
||||
import consola from 'consola'
|
||||
import { extendTarget, writeFile } from '../utils'
|
||||
import { SLSOptions, SLSTarget } from '../config'
|
||||
import { worker } from './worker'
|
||||
|
||||
export const cloudflare: SLSTarget = extendTarget(worker, {
|
||||
entry: '{{ runtimeDir }}/targets/cloudflare',
|
||||
outName: '_nuxt.js',
|
||||
generateIgnore: [
|
||||
'wrangler.toml'
|
||||
],
|
||||
hooks: {
|
||||
async done ({ targetDir }: SLSOptions) {
|
||||
await writeFile(resolve(targetDir, 'package.json'), JSON.stringify({ private: true, main: './_nuxt.js' }, null, 2))
|
||||
await writeFile(resolve(targetDir, 'package-lock.json'), JSON.stringify({ lockfileVersion: 1 }, null, 2))
|
||||
|
||||
consola.success('Ready to run `wrangler publish`')
|
||||
}
|
||||
}
|
||||
})
|
@ -1,8 +0,0 @@
|
||||
|
||||
import { SLSTarget } from '../config'
|
||||
|
||||
export const lambda: SLSTarget = {
|
||||
entry: '{{ runtimeDir }}/targets/lambda',
|
||||
outName: '_nuxt.js',
|
||||
inlineChunks: false
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { extendTarget } from '../utils'
|
||||
import { SLSTarget } from '../config'
|
||||
import { lambda } from './lambda'
|
||||
|
||||
export const netlify: SLSTarget = extendTarget(lambda, {
|
||||
outName: '_nuxt.js',
|
||||
generateIgnore: [
|
||||
'netlify.toml',
|
||||
'_redirects'
|
||||
]
|
||||
})
|
@ -1,8 +0,0 @@
|
||||
import { SLSTarget } from '../config'
|
||||
|
||||
export const node: SLSTarget = {
|
||||
entry: '{{ runtimeDir }}/targets/node',
|
||||
outName: 'index.js',
|
||||
inlineChunks: false,
|
||||
minify: true
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
import { resolve } from 'path'
|
||||
import { extendTarget, writeFile } from '../utils'
|
||||
import { SLSTarget } from '../config'
|
||||
import { node } from './node'
|
||||
|
||||
export const vercel: SLSTarget = extendTarget(node, {
|
||||
targetDir: '{{ rootDir }}/.vercel_build_output',
|
||||
outName: 'functions/node/_nuxt/index.js',
|
||||
publicDir: '{{ targetDir }}/static',
|
||||
cleanTargetDir: false,
|
||||
generateIgnore: [
|
||||
'vercel.json'
|
||||
],
|
||||
hooks: {
|
||||
async done ({ targetDir }) {
|
||||
await writeRoutes({ targetDir })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function writeRoutes ({ targetDir }) {
|
||||
const routes = [
|
||||
{
|
||||
src: '/sw.js',
|
||||
headers: {
|
||||
'cache-control': 'public, max-age=0, must-revalidate'
|
||||
},
|
||||
continue: true
|
||||
},
|
||||
{
|
||||
src: '/_nuxt/(.*)',
|
||||
headers: {
|
||||
'cache-control': 'public,max-age=31536000,immutable'
|
||||
},
|
||||
continue: true
|
||||
},
|
||||
{
|
||||
handle: 'filesystem'
|
||||
},
|
||||
{
|
||||
src: '(.*)',
|
||||
dest: '/.vercel/functions/_nuxt/index'
|
||||
}
|
||||
]
|
||||
|
||||
await writeFile(resolve(targetDir, 'config/routes.json'), JSON.stringify(routes, null, 2))
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { SLSTarget } from '../config'
|
||||
|
||||
export const worker: SLSTarget = {
|
||||
entry: null, // Abstract
|
||||
node: false,
|
||||
minify: true,
|
||||
hooks: {
|
||||
'rollup:before' ({ rollupConfig }) {
|
||||
rollupConfig.output.format = 'iife'
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,17 @@
|
||||
import { relative, dirname, resolve } from 'path'
|
||||
import { relative, dirname, resolve } from 'upath'
|
||||
import fse from 'fs-extra'
|
||||
import jiti from 'jiti'
|
||||
import defu from 'defu'
|
||||
import Hookable from 'hookable'
|
||||
import consola from 'consola'
|
||||
import type { SLSOptions, UnresolvedPath, SLSTarget, SLSTargetFn, SLSConfig } from '../config'
|
||||
import chalk from 'chalk'
|
||||
import { get } from 'dot-prop'
|
||||
import type { SigmaPreset, SigmaInput } from '../context'
|
||||
|
||||
export const MODULE_DIR = resolve(__dirname, '..')
|
||||
|
||||
export function hl (str: string) {
|
||||
return '`' + str + '`'
|
||||
return chalk.cyan(str)
|
||||
}
|
||||
|
||||
export function prettyPath (p: string, highlight = true) {
|
||||
@ -18,7 +20,13 @@ export function prettyPath (p: string, highlight = true) {
|
||||
}
|
||||
|
||||
export function compileTemplate (contents: string) {
|
||||
return (params: Record<string, any>) => contents.replace(/{{ ?(\w+) ?}}/g, (_, match) => params[match] || '')
|
||||
return (params: Record<string, any>) => contents.replace(/{{ ?([\w.]+) ?}}/g, (_, match) => {
|
||||
const val = get(params, match)
|
||||
if (!val) {
|
||||
consola.warn(`cannot resolve template param '${match}' in ${contents.substr(0, 20)}`)
|
||||
}
|
||||
return val as string || `${match}`
|
||||
})
|
||||
}
|
||||
|
||||
export function serializeTemplate (contents: string) {
|
||||
@ -42,16 +50,16 @@ export async function writeFile (file, contents) {
|
||||
consola.info('Generated', prettyPath(file))
|
||||
}
|
||||
|
||||
export function resolvePath (options: SLSOptions, path: UnresolvedPath, resolveBase: string = '') {
|
||||
export function resolvePath (sigmaContext: SigmaInput, path: string | ((sigmaContext) => string), resolveBase: string = ''): string {
|
||||
if (typeof path === 'function') {
|
||||
path = path(options)
|
||||
path = path(sigmaContext)
|
||||
}
|
||||
|
||||
if (typeof path !== 'string') {
|
||||
throw new TypeError('Invalid path: ' + path)
|
||||
}
|
||||
|
||||
path = compileTemplate(path)(options)
|
||||
path = compileTemplate(path)(sigmaContext)
|
||||
|
||||
return resolve(resolveBase, path)
|
||||
}
|
||||
@ -64,22 +72,19 @@ export function detectTarget () {
|
||||
if (process.env.NOW_BUILDER) {
|
||||
return 'vercel'
|
||||
}
|
||||
|
||||
return 'node'
|
||||
}
|
||||
|
||||
export function extendTarget (base: SLSTarget, target: SLSTarget): SLSTargetFn {
|
||||
return (config: SLSConfig) => {
|
||||
if (typeof target === 'function') {
|
||||
target = target(config)
|
||||
export function extendPreset (base: SigmaPreset, preset: SigmaPreset): SigmaPreset {
|
||||
return (config: SigmaInput) => {
|
||||
if (typeof preset === 'function') {
|
||||
preset = preset(config)
|
||||
}
|
||||
if (typeof base === 'function') {
|
||||
base = base(config)
|
||||
}
|
||||
return defu({
|
||||
hooks: Hookable.mergeHooks(base.hooks, target.hooks),
|
||||
nuxtHooks: Hookable.mergeHooks(base.nuxtHooks as any, target.nuxtHooks as any)
|
||||
}, target, base)
|
||||
hooks: Hookable.mergeHooks(base.hooks, preset.hooks)
|
||||
}, preset, base)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { resolve, dirname, relative } from 'path'
|
||||
import { resolve, dirname, relative } from 'upath'
|
||||
import globby from 'globby'
|
||||
import prettyBytes from 'pretty-bytes'
|
||||
import gzipSize from 'gzip-size'
|
||||
@ -30,5 +30,5 @@ export async function printFSTree (dir) {
|
||||
totalGzip += item.gzip
|
||||
})
|
||||
|
||||
process.stdout.write(`${chalk.cyan('λ Total size:')} ${prettyBytes(totalSize)} (${prettyBytes(totalGzip)} gzip)\n`)
|
||||
process.stdout.write(`${chalk.cyan('Σ Total size:')} ${prettyBytes(totalSize)} (${prettyBytes(totalGzip)} gzip)\n`)
|
||||
}
|
||||
|
7
packages/nitro/src/utils/wpfs.ts
Normal file
7
packages/nitro/src/utils/wpfs.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { join } from 'upath'
|
||||
import fsExtra from 'fs-extra'
|
||||
|
||||
export default {
|
||||
...fsExtra,
|
||||
join
|
||||
}
|
Loading…
Reference in New Issue
Block a user