refactor: move targets to src, remove template and improve runtime

This commit is contained in:
Pooya Parsa 2020-11-06 10:51:35 +01:00
parent ddccc9cb78
commit 47d7644b72
12 changed files with 154 additions and 52 deletions

View File

@ -5,9 +5,9 @@ import Hookable from 'hookable'
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 { emptyDir } from 'fs-extra' import { emptyDir, readFile } from 'fs-extra'
import { getRollupConfig } from './rollup/config' import { getRollupConfig } from './rollup/config'
import { hl, prettyPath, renderTemplate, compileTemplateToJS, writeFileP } from './utils' import { hl, prettyPath, serializeTemplate, writeFileP } from './utils'
import { SLSOptions } from './config' import { SLSOptions } from './config'
export async function build (options: SLSOptions) { export async function build (options: SLSOptions) {
@ -18,13 +18,11 @@ export async function build (options: SLSOptions) {
hooks.addHooks(options.hooks) hooks.addHooks(options.hooks)
// Compile html template // Compile html template
const htmlTemplate = { const htmlSrc = resolve(options.buildDir, `views/${{ 2: 'app', 3: 'document' }[options.nuxt]}.template.html`)
src: resolve(options.buildDir, `views/${{ 2: 'app', 3: 'document' }[options.nuxt]}.template.html`), const htmlTemplate = { src: htmlSrc, contents: '', dst: '', compiled: '' }
dst: '',
compiled: ''
}
htmlTemplate.dst = htmlTemplate.src.replace(/.html$/, '.js').replace('app.', 'document.') htmlTemplate.dst = htmlTemplate.src.replace(/.html$/, '.js').replace('app.', 'document.')
htmlTemplate.compiled = await compileTemplateToJS(htmlTemplate.src) htmlTemplate.contents = await readFile(htmlTemplate.src, 'utf-8')
htmlTemplate.compiled = serializeTemplate(htmlTemplate.src)
await hooks.callHook('template:document', htmlTemplate) await hooks.callHook('template:document', htmlTemplate)
await writeFileP(htmlTemplate.dst, htmlTemplate.compiled) await writeFileP(htmlTemplate.dst, htmlTemplate.compiled)
consola.info('Generated', prettyPath(htmlTemplate.dst)) consola.info('Generated', prettyPath(htmlTemplate.dst))
@ -42,15 +40,5 @@ export async function build (options: SLSOptions) {
chalk.gray(`(Size: ${size} Gzip: ${zSize})`) chalk.gray(`(Size: ${size} Gzip: ${zSize})`)
) )
for (const tmpl of options.templates) {
let dst = tmpl.dst
if (typeof dst === 'function') {
dst = dst(options)
}
const dstPath = resolve(options.targetDir, dst)
await renderTemplate(tmpl.src, dstPath, { options })
consola.info('Compiled', prettyPath(dstPath))
}
await hooks.callHook('done', options) await hooks.callHook('done', options)
} }

View File

@ -1,14 +1,15 @@
import { resolve } from 'path' import { resolve } from 'path'
import defu from 'defu' import defu from 'defu'
import { NuxtOptions } from '@nuxt/types' import { NuxtOptions } from '@nuxt/types'
import { tryImport, LIB_DIR, resolvePath, detectTarget } from './utils' import { tryImport, resolvePath, detectTarget } from './utils'
import * as TARGETS from './targets'
export type UnresolvedPath = string | ((SLSOptions) => string) export type UnresolvedPath = string | ((SLSOptions) => string)
export interface SLSOptions { export interface SLSOptions {
node: false node: false
target: string target: string
entry: string entry: UnresolvedPath
slsDir: string slsDir: string
outName: string outName: string
logStartup: boolean logStartup: boolean
@ -18,7 +19,7 @@ export interface SLSOptions {
staticDir: string staticDir: string
targetDir: string targetDir: string
rootDir: string rootDir: string
templates: { src: string, dst: UnresolvedPath }[] runtimeDir: string
static: string[] static: string[]
renderer: string renderer: string
nuxt: 2 | 3 nuxt: 2 | 3
@ -33,13 +34,15 @@ export interface SLSOptions {
version: string version: string
} }
hooks: { [key: string]: any } // TODO: export from hookable hooks: { [key: string]: any } // TODO: export from hookable
nuxtHooks: NuxtOptions['hooks'] nuxtHooks: { [key: string]: Function } // NuxtOptions['hooks']
} }
export interface SLSConfig extends Omit<Partial<SLSOptions>, 'targetDir'> { export interface SLSConfig extends Omit<Partial<SLSOptions>, 'targetDir'> {
targetDir: UnresolvedPath targetDir: UnresolvedPath
} }
export type SLSTarget = Partial<SLSConfig>
export function getoptions (nuxtOptions: NuxtOptions): SLSOptions { export function getoptions (nuxtOptions: NuxtOptions): SLSOptions {
const defaults: SLSConfig = { const defaults: SLSConfig = {
rootDir: nuxtOptions.rootDir, rootDir: nuxtOptions.rootDir,
@ -49,7 +52,7 @@ export function getoptions (nuxtOptions: NuxtOptions): SLSOptions {
// @ts-ignore // @ts-ignore
staticAssets: nuxtOptions.generate.staticAssets, staticAssets: nuxtOptions.generate.staticAssets,
outName: 'server.js', outName: 'server.js',
templates: [], runtimeDir: resolve(__dirname, '../runtime'),
static: [], static: [],
nuxt: 2, nuxt: 2,
logStartup: true, logStartup: true,
@ -61,7 +64,7 @@ export function getoptions (nuxtOptions: NuxtOptions): SLSOptions {
if (typeof target === 'function') { if (typeof target === 'function') {
target = target(nuxtOptions) target = target(nuxtOptions)
} }
let targetDefaults = tryImport(LIB_DIR, `./targets/${target}`) || tryImport(nuxtOptions.rootDir, target) let targetDefaults = TARGETS[target] || tryImport(nuxtOptions.rootDir, target)
if (!targetDefaults) { if (!targetDefaults) {
throw new Error('Cannot resolve target: ' + target) throw new Error('Cannot resolve target: ' + target)
} }

View File

@ -10,7 +10,7 @@ import replace from '@rollup/plugin-replace'
import analyze from 'rollup-plugin-analyzer' import analyze from 'rollup-plugin-analyzer'
import { SLSOptions } from '../config' import { SLSOptions } from '../config'
import { RUNTIME_DIR } from '../utils' import { resolvePath } from '../utils'
import dynamicRequire from './dynamic-require' import dynamicRequire from './dynamic-require'
export type RollupConfig = InputOptions & { output: OutputOptions } export type RollupConfig = InputOptions & { output: OutputOptions }
@ -50,7 +50,7 @@ export const getRollupConfig = (config: SLSOptions) => {
} }
const options: RollupConfig = { const options: RollupConfig = {
input: config.entry, input: resolvePath(config, config.entry),
output: { output: {
file: resolve(config.targetDir, config.outName), file: resolve(config.targetDir, config.outName),
format: 'cjs', format: 'cjs',
@ -96,10 +96,10 @@ export const getRollupConfig = (config: SLSOptions) => {
const renderer = config.renderer || (config.nuxt === 2 ? 'vue2' : 'vue3') const renderer = config.renderer || (config.nuxt === 2 ? 'vue2' : 'vue3')
options.plugins.push(alias({ options.plugins.push(alias({
entries: { entries: {
'~runtime': RUNTIME_DIR, '~runtime': config.runtimeDir,
'~renderer': require.resolve(resolve(RUNTIME_DIR, renderer)), '~renderer': require.resolve(resolve(config.runtimeDir, renderer)),
'~build': config.buildDir, '~build': config.buildDir,
'~mock': require.resolve(resolve(RUNTIME_DIR, 'mock')), '~mock': require.resolve(resolve(config.runtimeDir, 'mock')),
...mocks.reduce((p, c) => ({ ...p, [c]: '~mock' }), {}), ...mocks.reduce((p, c) => ({ ...p, [c]: '~mock' }), {}),
...providedDeps.reduce((p, c) => ({ ...p, [c]: require.resolve(c) }), {}) ...providedDeps.reduce((p, c) => ({ ...p, [c]: require.resolve(c) }), {})
} }

View File

@ -0,0 +1,34 @@
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'
const getScriptTag = () => '<script async defer src="/sw-register.js"></script>'
export const browser: SLSTarget = extendTarget(worker, {
targetDir: ({ publicDir }) => publicDir,
nuxtHooks: {
'vue-renderer:ssr:templateParams' (params) {
params.APP += getScriptTag()
},
'vue-renderer:spa:templateParams' (params) {
params.APP += getScriptTag()
}
},
hooks: {
'template:document' (tmpl) {
tmpl.compiled = tmpl.compiled.replace('</body>', getScriptTag() + '</body>')
},
async done ({ targetDir, publicDir }) {
const rootIndex = resolve(publicDir, 'index.html')
const rootFallback = resolve(publicDir, '200.html')
if (!existsSync(rootIndex) && existsSync(rootFallback)) {
await copy(rootFallback, rootIndex)
}
consola.info(`Try with \`npx serve ${relative(process.cwd(), targetDir)}\``)
}
}
})

View File

@ -0,0 +1,7 @@
import { extendTarget } from '../utils'
import { SLSTarget } from '../config'
import { worker } from './worker'
export const cloudflare: SLSTarget = extendTarget(worker, {
})

View File

@ -0,0 +1,7 @@
export * from './browser'
export * from './cloudflare'
export * from './lambda'
export * from './netlify'
export * from './node'
export * from './vercel'
export * from './worker'

View File

@ -0,0 +1,14 @@
import { relative } from 'path'
import consola from 'consola'
import { SLSTarget } from '../config'
export const lambda: SLSTarget = {
entry: '{{ runtimeDir }}/lambda',
hooks: {
'done' ({ rollupConfig }) {
const entry = relative(process.cwd(), rollupConfig.output.file).replace(/\.js$/, '')
consola.info(`Ready to deploy lambda: \`${entry}\``)
}
}
}

View File

@ -0,0 +1,7 @@
import { extendTarget } from '../utils'
import { SLSTarget } from '../config'
import { lambda } from './lambda'
export const netlify: SLSTarget = extendTarget(lambda, {
})

View File

@ -0,0 +1,14 @@
import { relative } from 'path'
import consola from 'consola'
import { SLSTarget } from '../config'
export const node: SLSTarget = {
entry: '{{ runtimeDir }}/node',
hooks: {
'done' ({ rollupConfig }) {
const entry = relative(process.cwd(), rollupConfig.output.file).replace(/\.js$/, '')
consola.info(`Ready to deploy lambda: \`${entry}\``)
consola.info(`You can try using \`node ${entry} [path]\``)
}
}
}

View File

@ -0,0 +1,7 @@
import { extendTarget } from '../utils'
import { SLSTarget } from '../config'
import { node } from './node'
export const vercel: SLSTarget = extendTarget(node, {
})

View File

@ -0,0 +1,14 @@
import { SLSTarget } from '../config'
export const worker: SLSTarget = {
entry: '{{ runtimeDir }}/worker',
node: false,
hooks: {
'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'
}
}
}

View File

@ -1,31 +1,23 @@
import { relative, dirname, resolve } from 'path' import { relative, dirname, resolve } from 'path'
import { readFile, writeFile, mkdirp } from 'fs-extra' import { writeFile, mkdirp } from 'fs-extra'
import jiti from 'jiti' import jiti from 'jiti'
import { SLSOptions, UnresolvedPath } from './config' import defu from 'defu'
import { SLSOptions, UnresolvedPath, SLSTarget } from './config'
const pwd = process.cwd() export function hl (str: string) {
return '`' + str + '`'
export const hl = (str: string) => '`' + str + '`' }
export function prettyPath (p: string, highlight = true) { export function prettyPath (p: string, highlight = true) {
p = relative(pwd, p) p = relative(process.cwd(), p)
return highlight ? hl(p) : p return highlight ? hl(p) : p
} }
export async function loadTemplate (src: string) { export function compileTemplate (contents: string) {
const contents = await readFile(src, 'utf-8') return (params: Record<string, any>) => contents.replace(/{{ ?(\w+) ?}}/g, (_, match) => params[match] || '')
return (params: Record<string, string>) => contents.replace(/{{ (\w+) }}/g, `${params.$1}`)
} }
export async function renderTemplate (src: string, dst: string, params: any) { export function serializeTemplate (contents: string) {
const tmpl = await loadTemplate(src)
const rendered = tmpl(params)
await mkdirp(dirname(dst))
await writeFile(dst, rendered)
}
export async function compileTemplateToJS (src: string) {
const contents = await readFile(src, 'utf-8')
// eslint-disable-next-line no-template-curly-in-string // eslint-disable-next-line no-template-curly-in-string
return `export default (params) => \`${contents.replace(/{{ (\w+) }}/g, '${params.$1}')}\`` return `export default (params) => \`${contents.replace(/{{ (\w+) }}/g, '${params.$1}')}\``
} }
@ -35,11 +27,24 @@ export async function writeFileP (path, contents) {
await writeFile(path, contents) await writeFile(path, contents)
} }
export const jitiImport = (dir: string, path: string) => jiti(dir)(path) export function jitiImport (dir: string, path: string) {
export const tryImport = (dir: string, path: string) => { try { return jitiImport(dir, path) } catch (_err) { } } return jiti(dir)(path)
}
export function tryImport (dir: string, path: string) {
try {
return jitiImport(dir, path)
} catch (_err) { }
}
export function resolvePath (options: SLSOptions, path: UnresolvedPath, resolveBase: string = '') { export function resolvePath (options: SLSOptions, path: UnresolvedPath, resolveBase: string = '') {
return resolve(resolveBase, typeof path === 'string' ? path : path(options)) if (typeof path === 'function') {
path = path(options)
}
path = compileTemplate(path)(options)
return resolve(resolveBase, path)
} }
export function detectTarget () { export function detectTarget () {
@ -54,5 +59,7 @@ export function detectTarget () {
return 'node' return 'node'
} }
export const LIB_DIR = resolve(__dirname, '../lib') export function extendTarget (base: SLSTarget, target: SLSTarget): SLSTarget {
export const RUNTIME_DIR = resolve(LIB_DIR, 'runtime') // TODO: merge hooks
return defu(target, base)
}