refactror: separate lib

This commit is contained in:
Pooya Parsa 2020-11-04 14:15:38 +01:00
parent 05e8d538db
commit 5538f342f7
19 changed files with 51 additions and 331 deletions

View File

@ -2,33 +2,18 @@ import { resolve } from 'path'
import consola from 'consola'
import { rollup, OutputOptions } from 'rollup'
import Hookable from 'hookable'
import defu from 'defu'
import prettyBytes from 'pretty-bytes'
import gzipSize from 'gzip-size'
import chalk from 'chalk'
import { copy, emptyDir, existsSync } from 'fs-extra'
import { getRollupConfig } from './rollup/config'
import { tryImport, hl, prettyPath, renderTemplate, compileTemplateToJS } from './utils'
import { getTargetConfig } from './config'
import { hl, prettyPath, renderTemplate, compileTemplateToJS } from './utils'
export async function build (baseConfig, target) {
consola.info(`Generating bundle for ${hl(target.target)}`)
const _targetDefaults = tryImport(__dirname, `./targets/${target.target}`) ||
tryImport(baseConfig.rootDir, target.target)
if (!_targetDefaults) {
throw new Error('Cannot resolve target: ' + target.target)
}
const config: any = defu(
// Target specific config by user
target,
// Global user config
baseConfig,
// Target defaults
_targetDefaults,
// Generic defaults
{ outDir: resolve(baseConfig.buildDir, `dist/${target.target}`), outName: 'index.js' }
)
const config: any = getTargetConfig(baseConfig, target)
const hooks = new Hookable()
hooks.addHooks(config.hooks)

View File

@ -1,9 +1,8 @@
import { resolve } from 'path'
import consola from 'consola'
import { build, compileHTMLTemplate, ensureDist } from './build'
import { getBaseConfig } from './config'
async function _runCLI () {
export async function runCLI () {
const rootDir = resolve(process.cwd(), process.argv[2] || '.')
// Config
@ -23,10 +22,3 @@ async function _runCLI () {
await build(baseConfig, target)
}
}
export function runCLI () {
_runCLI().catch((err) => {
consola.error(err)
process.exit(1)
})
}

View File

@ -1,8 +1,9 @@
import { resolve } from 'path'
import { tryImport } from './utils'
import defu from 'defu'
import { tryImport, LIB_DIR } from './utils'
export function getBaseConfig (rootDir) {
const baseConfig = {
let baseConfig = {
rootDir,
buildDir: '',
targets: [],
@ -14,7 +15,13 @@ export function getBaseConfig (rootDir) {
logStartup: true
}
Object.assign(baseConfig, tryImport(rootDir, './nuxt.config')!.serverless)
const nuxtConfig = tryImport(rootDir, './nuxt.config')
if (!nuxtConfig) {
throw new Error('`nuxt.config` file not found in: ' + rootDir)
}
if (nuxtConfig.serverless) {
baseConfig = defu(nuxtConfig.serverless, baseConfig)
}
baseConfig.buildDir = resolve(baseConfig.rootDir, baseConfig.buildDir || '.nuxt')
@ -25,3 +32,24 @@ export function getBaseConfig (rootDir) {
return baseConfig
}
export function getTargetConfig (baseConfig, target) {
const _targetDefaults = tryImport(LIB_DIR, `./targets/${target.target}`) ||
tryImport(baseConfig.rootDir, target.target)
if (!_targetDefaults) {
throw new Error('Cannot resolve target: ' + target.target)
}
// TODO: Merge hooks
return defu(
// Target specific config by user
target,
// Global user config
baseConfig,
// Target defaults
_targetDefaults,
// Generic defaults
{ outDir: resolve(baseConfig.buildDir, `dist/${target.target}`), outName: 'index.js' }
)
}

View File

@ -0,0 +1,6 @@
require('../dist').runCLI().catch((error) => {
const consola = require('consola')
consola.error(error)
process.exit(1)
})

View File

@ -8,7 +8,7 @@ import alias from '@rollup/plugin-alias'
import json from '@rollup/plugin-json'
import replace from '@rollup/plugin-replace'
import analyze from 'rollup-plugin-analyzer'
import ts from 'rollup-plugin-ts'
import { RUNTIME_DIR } from '../utils'
import dynamicRequire from './dynamic-require'
export type RollupConfig = InputOptions & { output: OutputOptions }
@ -83,22 +83,14 @@ export const getRollupConfig = (config) => {
const renderer = config.renderer || (config.nuxt === 2 ? 'vue2' : 'vue3')
options.plugins.push(alias({
entries: {
'~runtime': path.resolve(__dirname, '../runtime'),
'~renderer': require.resolve('../runtime/' + renderer),
'~runtime': RUNTIME_DIR,
'~renderer': require.resolve(path.resolve(RUNTIME_DIR, renderer)),
'~build': config.buildDir,
'~mock': require.resolve('../runtime/mock'),
'~mock': require.resolve(path.resolve(RUNTIME_DIR, 'mock')),
...mocks.reduce((p, c) => ({ ...p, [c]: '~mock' }), {})
}
}))
// https://github.com/wessberg/rollup-plugin-ts
options.plugins.push(ts({
transpileOnly: true,
transpiler: 'babel',
include: ['**/*.ts'],
exclude: ['*.json', 'node_modules']
}))
// https://github.com/rollup/plugins/tree/master/packages/node-resolve
options.plugins.push(resolve({
extensions,

View File

@ -1,25 +0,0 @@
function getProxy (name) {
const fn = function () { }
fn.prototype.name = name
const props = {}
return new Proxy(fn, {
get (_target, prop) {
if (prop === 'caller') { return null }
return (props[prop] = props[prop] || getProxy(`${name}.${prop.toString()}`))
},
apply (_target, _this, _args) {
console.debug(`${name}(...)`)
return getProxy(`${name}()`)
},
construct (_target, _args, _newT) {
return getProxy(`[${name}]`)
},
enumerate (_target) {
return []
}
})
}
module.exports = getProxy('mock')

View File

@ -1,55 +0,0 @@
import { createRenderer } from 'vue-bundle-renderer'
import devalue from '@nuxt/devalue'
// @ts-ignore
import { renderToString } from '~renderer'
// @ts-ignore
import server from '~build/dist/server/server'
// @ts-ignore
import clientManifest from '~build/dist/server/client.manifest.json'
// @ts-ignore
import htmlTemplate from '~build/views/document.template.js'
const renderer = createRenderer(server, {
clientManifest,
renderToString
})
export async function render (url, ctx: any) {
const start = process.hrtime()
const ssrContext: any = {
url,
runtimeConfig: {
public: {},
private: {}
},
...ctx
}
const rendered = await renderer.renderToString(ssrContext)
const state = `<script>window.__NUXT__=${devalue(ssrContext.nuxt /* nuxt 2 */ || ssrContext.payload /* nuxt 3 */)}</script>`
const _html = `<div id="__nuxt">${rendered.html}</div>`
const html = htmlTemplate({
HTML_ATTRS: '',
HEAD_ATTRS: '',
BODY_ATTRS: '',
HEAD: rendered.renderResourceHints() + rendered.renderStyles() + (ssrContext.styles || ''),
APP: _html + state + rendered.renderScripts()
})
const end = process.hrtime(start)
const time = ((end[0] * 1e9) + end[1]) / 1e6
return {
html,
status: 200,
headers: {
'Content-Type': 'text/html;charset=UTF-8',
// @ts-ignore
'X-Nuxt-Coldstart': global._coldstart + 'ms',
'X-Nuxt-ResponseTime': time + 'ms'
}
}
}

View File

@ -1,14 +0,0 @@
import { createRenderer } from 'vue-server-renderer/build.prod.js'
const _renderer = createRenderer({})
export function renderToString (component, context) {
return new Promise((resolve, reject) => {
_renderer.renderToString(component, context, (err, result) => {
if (err) {
return reject(err)
}
return resolve(result)
})
})
}

View File

@ -1,2 +0,0 @@
// @ts-ignore
export { renderToString } from '@vue/server-renderer'

View File

@ -1,16 +0,0 @@
// @ts-ignore
import { render } from '~runtime/server'
addEventListener('fetch', (event: any) => {
event.respondWith(handleEvent(event.request))
})
async function handleEvent (request) {
try {
const url = new URL(request.url)
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 })
}
}

View File

@ -1,12 +0,0 @@
export default {
entry: require.resolve('./entry'),
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,27 +0,0 @@
// @ts-ignore
import { render } from '~runtime/server'
// @ts-ignore
export { render } from '~runtime/server'
async function cli () {
const url = process.argv[2] || '/'
const debug = (label, ...args) => console.debug(`> ${label}:`, ...args)
const { html, status, headers } = await render(url)
debug('URL', url)
debug('Status', status)
for (const header in headers) {
debug(header, headers[header])
}
console.log('\n', html)
}
if (require.main === module) {
cli().catch((err) => {
console.error(err)
process.exit(1)
})
}

View File

@ -1,11 +0,0 @@
import consola from 'consola'
export default {
entry: require.resolve('./entry'),
hooks: {
'done' ({ rollupConfig }) {
consola.info(`Usage: \`node ${rollupConfig.output.file} [route]\``)
}
}
}

View File

@ -1,31 +0,0 @@
// @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))
})
self.addEventListener('install', () => {
// @ts-ignore
self.skipWaiting()
})
self.addEventListener('activate', (event) => {
// @ts-ignore
event.waitUntil(self.clients.claim())
})
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 })
}
}

View File

@ -1,42 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<!-- SW_INIT-->
<meta charset="utf-8">
<link rel="prefetch" href="/nuxt.sw.js">
<script>
async function reload() {
const html = await fetch(window.location.href).then(r => r.text())
if (html.includes('<!-- SW_INIT-->')) {
window.location.reload(false)
} else {
document.open()
document.write(html)
document.close()
}
}
async function register() {
const registration = await navigator.serviceWorker.register('/nuxt.sw.js')
await navigator.serviceWorker.ready
registration.active.addEventListener('statechange', (event) => {
if (event.target.state === 'activated') {
reload()
}
})
}
if (location.hostname !== 'localhost' && location.protocol === 'http:') {
location.replace(location.href.replace('http://', 'https://'))
} else {
register()
}
</script>
</head>
<body>
Loading...
</body>
</html>

View File

@ -1,24 +0,0 @@
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: {
'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}\``)
}
}
}

View File

@ -1,17 +0,0 @@
// @ts-ignore
import { render } from '~runtime/server'
module.exports = async (req, res) => {
try {
const { html, status, headers } = await render(req.url, { req, res })
for (const header in headers) {
res.setHeader(header, headers[header])
}
res.status(status)
res.end(html)
} catch (error) {
console.error(error)
res.status(500)
res.end('Internal Error: ' + error)
}
}

View File

@ -1,11 +0,0 @@
import consola from 'consola'
export default {
entry: require.resolve('./entry'),
dynamicImporter: false,
hooks: {
'done' () {
consola.info('Run `vercel serverless` to serverless!')
}
}
}

View File

@ -1,4 +1,4 @@
import { relative, dirname } from 'path'
import { relative, dirname, resolve } from 'path'
import { readFile, writeFile, mkdirp } from 'fs-extra'
import jiti from 'jiti'
@ -31,4 +31,8 @@ export async function compileTemplateToJS (src: string, dst: string) {
await writeFile(dst, compiled)
}
export const tryImport = (dir, path) => { try { return jiti(dir)(path) } catch (_err) { } }
export const jitiImport = (dir, path) => jiti(dir)(path)
export const tryImport = (dir, path) => { try { return jitiImport(dir, path) } catch (_err) { } }
export const LIB_DIR = resolve(__dirname, '../lib')
export const RUNTIME_DIR = resolve(LIB_DIR, 'runtime')