mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-29 17:07:22 +00:00
feat(nitro): server assets (#83)
This commit is contained in:
parent
31f06e9f69
commit
babb70a4bd
@ -7,6 +7,7 @@ import { tryImport, resolvePath, detectTarget, extendPreset } from './utils'
|
|||||||
import * as PRESETS from './presets'
|
import * as PRESETS from './presets'
|
||||||
import type { NodeExternalsOptions } from './rollup/plugins/externals'
|
import type { NodeExternalsOptions } from './rollup/plugins/externals'
|
||||||
import type { StorageOptions } from './rollup/plugins/storage'
|
import type { StorageOptions } from './rollup/plugins/storage'
|
||||||
|
import type { AssetOptions } from './rollup/plugins/assets'
|
||||||
import type { ServerMiddleware } from './server/middleware'
|
import type { ServerMiddleware } from './server/middleware'
|
||||||
|
|
||||||
export interface NitroContext {
|
export interface NitroContext {
|
||||||
@ -34,6 +35,7 @@ export interface NitroContext {
|
|||||||
publicDir: string
|
publicDir: string
|
||||||
}
|
}
|
||||||
storage: StorageOptions,
|
storage: StorageOptions,
|
||||||
|
assets: AssetOptions,
|
||||||
_nuxt: {
|
_nuxt: {
|
||||||
majorVersion: number
|
majorVersion: number
|
||||||
dev: boolean
|
dev: boolean
|
||||||
@ -88,6 +90,10 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N
|
|||||||
publicDir: '{{ output.dir }}/public'
|
publicDir: '{{ output.dir }}/public'
|
||||||
},
|
},
|
||||||
storage: { mounts: { } },
|
storage: { mounts: { } },
|
||||||
|
assets: {
|
||||||
|
inline: !nuxtOptions.dev,
|
||||||
|
dirs: {}
|
||||||
|
},
|
||||||
_nuxt: {
|
_nuxt: {
|
||||||
majorVersion: nuxtOptions._majorVersion || 2,
|
majorVersion: nuxtOptions._majorVersion || 2,
|
||||||
dev: nuxtOptions.dev,
|
dev: nuxtOptions.dev,
|
||||||
@ -148,6 +154,11 @@ export function getNitroContext (nuxtOptions: NuxtOptions, input: NitroInput): N
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assets
|
||||||
|
nitroContext.assets.dirs.server = {
|
||||||
|
dir: resolve(nitroContext._nuxt.rootDir, 'server/assets'), meta: true
|
||||||
|
}
|
||||||
|
|
||||||
// console.log(nitroContext)
|
// console.log(nitroContext)
|
||||||
// process.exit(1)
|
// process.exit(1)
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import { externals } from './plugins/externals'
|
|||||||
import { timing } from './plugins/timing'
|
import { timing } from './plugins/timing'
|
||||||
import { autoMock } from './plugins/automock'
|
import { autoMock } from './plugins/automock'
|
||||||
import { staticAssets, dirnames } from './plugins/static'
|
import { staticAssets, dirnames } from './plugins/static'
|
||||||
|
import { assets } from './plugins/assets'
|
||||||
import { middleware } from './plugins/middleware'
|
import { middleware } from './plugins/middleware'
|
||||||
import { esbuild } from './plugins/esbuild'
|
import { esbuild } from './plugins/esbuild'
|
||||||
import { raw } from './plugins/raw'
|
import { raw } from './plugins/raw'
|
||||||
@ -81,8 +82,10 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
prefix = 'nuxt'
|
prefix = 'nuxt'
|
||||||
} else if (lastModule.startsWith(nitroContext._internal.runtimeDir)) {
|
} else if (lastModule.startsWith(nitroContext._internal.runtimeDir)) {
|
||||||
prefix = 'nitro'
|
prefix = 'nitro'
|
||||||
} else if (!prefix && nitroContext.middleware.find(m => lastModule.startsWith(m.handle))) {
|
} else if (!prefix && nitroContext.middleware.find(m => lastModule.startsWith(m.handle as string))) {
|
||||||
prefix = 'middleware'
|
prefix = 'middleware'
|
||||||
|
} else if (lastModule.includes('assets')) {
|
||||||
|
prefix = 'assets'
|
||||||
}
|
}
|
||||||
return join('chunks', prefix, '[name].js')
|
return join('chunks', prefix, '[name].js')
|
||||||
},
|
},
|
||||||
@ -148,7 +151,11 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
|||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// Assets
|
||||||
|
rollupConfig.plugins.push(assets(nitroContext.assets))
|
||||||
|
|
||||||
// Static
|
// Static
|
||||||
|
// TODO: use assets plugin
|
||||||
if (nitroContext.serveStatic) {
|
if (nitroContext.serveStatic) {
|
||||||
rollupConfig.plugins.push(dirnames())
|
rollupConfig.plugins.push(dirnames())
|
||||||
rollupConfig.plugins.push(staticAssets(nitroContext))
|
rollupConfig.plugins.push(staticAssets(nitroContext))
|
||||||
|
103
packages/nitro/src/rollup/plugins/assets.ts
Normal file
103
packages/nitro/src/rollup/plugins/assets.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import { readFile, stat } from 'fs/promises'
|
||||||
|
import type { Plugin } from 'rollup'
|
||||||
|
import createEtag from 'etag'
|
||||||
|
import mime from 'mime'
|
||||||
|
import { resolve } from 'upath'
|
||||||
|
import globby from 'globby'
|
||||||
|
import virtual from './virtual'
|
||||||
|
|
||||||
|
export interface AssetOptions {
|
||||||
|
inline: Boolean
|
||||||
|
dirs: {
|
||||||
|
[assetdir: string]: {
|
||||||
|
dir: string
|
||||||
|
meta?: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assets (opts: AssetOptions): Plugin {
|
||||||
|
type Asset = {
|
||||||
|
fsPath: string,
|
||||||
|
meta: {
|
||||||
|
type?: string,
|
||||||
|
etag?: string,
|
||||||
|
mtime?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const assetUtils = `
|
||||||
|
export function readAsset (id) {
|
||||||
|
return getAsset(id).read()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function statAsset (id) {
|
||||||
|
return getAsset(id).meta
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
if (!opts.inline) {
|
||||||
|
return virtual({
|
||||||
|
'~nitro/assets': `
|
||||||
|
import { statSync, promises as fsp } from 'fs'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
|
||||||
|
const dirs = ${JSON.stringify(opts.dirs)}
|
||||||
|
|
||||||
|
${assetUtils}
|
||||||
|
|
||||||
|
export function getAsset (id) {
|
||||||
|
for (const dirname in dirs) {
|
||||||
|
if (id.startsWith(dirname + '/')) {
|
||||||
|
const dirOpts = dirs[dirname]
|
||||||
|
const path = resolve(dirOpts.dir, id.substr(dirname.length + 1))
|
||||||
|
let stat = statSync(path)
|
||||||
|
const asset = {
|
||||||
|
read: () => fsp.readFile(path, 'utf-8'),
|
||||||
|
meta: {
|
||||||
|
mtime: stat.mtime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return asset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('Asset dir not found: ' + id)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return virtual({
|
||||||
|
'~nitro/assets': {
|
||||||
|
async load () {
|
||||||
|
const assets: Record<string, Asset> = {}
|
||||||
|
for (const assetdir in opts.dirs) {
|
||||||
|
const dirOpts = opts.dirs[assetdir]
|
||||||
|
const files = globby.sync('**/*.*', { cwd: dirOpts.dir, absolute: false })
|
||||||
|
for (const _id of files) {
|
||||||
|
const fsPath = resolve(dirOpts.dir, _id)
|
||||||
|
const id = assetdir + '/' + _id
|
||||||
|
assets[id] = { fsPath, meta: {} }
|
||||||
|
if (dirOpts.meta) {
|
||||||
|
let type = mime.getType(id) || 'text/plain'
|
||||||
|
if (type.startsWith('text')) { type += '; charset=utf-8' }
|
||||||
|
const etag = createEtag(await readFile(fsPath))
|
||||||
|
const mtime = await stat(fsPath).then(s => s.mtime.toJSON())
|
||||||
|
assets[id].meta = { type, etag, mtime }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const inlineAssets = `const assets = {\n${Object.keys(assets).map(id =>
|
||||||
|
` ['${id}']: {\n read: () => import('${assets[id].fsPath}'),\n meta: ${JSON.stringify(assets[id].meta)}\n }`
|
||||||
|
).join(',\n')}\n}`
|
||||||
|
return `${inlineAssets}\n${assetUtils}
|
||||||
|
export function getAsset (id) {
|
||||||
|
if (!assets[id]) {
|
||||||
|
throw new Error('Asset not found : ' + id)
|
||||||
|
}
|
||||||
|
return assets[id]
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -12,7 +12,8 @@ export function middleware (getMiddleware: () => ServerMiddleware[]) {
|
|||||||
let lastDump = ''
|
let lastDump = ''
|
||||||
|
|
||||||
return virtual({
|
return virtual({
|
||||||
'~serverMiddleware': () => {
|
'~serverMiddleware': {
|
||||||
|
load: () => {
|
||||||
const middleware = getMiddleware()
|
const middleware = getMiddleware()
|
||||||
|
|
||||||
if (!stdenv.test) {
|
if (!stdenv.test) {
|
||||||
@ -26,16 +27,17 @@ export function middleware (getMiddleware: () => ServerMiddleware[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
${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 => `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')}
|
${middleware.filter(m => m.lazy !== false).map(m => `const ${getImportId(m.handle)} = () => import('${m.handle}');`).join('\n')}
|
||||||
|
|
||||||
const middleware = [
|
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')}
|
${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
|
export default middleware
|
||||||
`
|
`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,10 @@ export function raw (opts: RawOptions = {}): Plugin {
|
|||||||
name: 'raw',
|
name: 'raw',
|
||||||
transform (code, id) {
|
transform (code, id) {
|
||||||
if (id[0] !== '\0' && extensions.has(extname(id))) {
|
if (id[0] !== '\0' && extensions.has(extname(id))) {
|
||||||
return `// ${id}\nexport default ${JSON.stringify(code)}`
|
return {
|
||||||
|
code: `// ${id}\nexport default ${JSON.stringify(code)}`,
|
||||||
|
map: null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,21 @@ import * as path from 'path'
|
|||||||
|
|
||||||
import { Plugin } from 'rollup'
|
import { Plugin } from 'rollup'
|
||||||
|
|
||||||
type UnresolvedModule = string | (() => string)
|
type VirtualModule = string | { load: () => string | Promise<string> }
|
||||||
|
|
||||||
export interface RollupVirtualOptions {
|
export interface RollupVirtualOptions {
|
||||||
[id: string]: UnresolvedModule;
|
[id: string]: VirtualModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PREFIX = '\0virtual:'
|
const PREFIX = '\0virtual:'
|
||||||
|
|
||||||
const resolveModule = (m: UnresolvedModule) => typeof m === 'function' ? m() : m
|
|
||||||
|
|
||||||
export default function virtual (modules: RollupVirtualOptions): Plugin {
|
export default function virtual (modules: RollupVirtualOptions): Plugin {
|
||||||
const resolvedIds = new Map<string, string |(() => string)>()
|
const _modules = new Map<string, VirtualModule>()
|
||||||
|
|
||||||
Object.keys(modules).forEach((id) => {
|
for (const [id, mod] of Object.entries(modules)) {
|
||||||
resolvedIds.set(path.resolve(id), modules[id])
|
_modules.set(id, mod)
|
||||||
})
|
_modules.set(path.resolve(id), mod)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'virtual',
|
name: 'virtual',
|
||||||
@ -30,20 +30,29 @@ export default function virtual (modules: RollupVirtualOptions): Plugin {
|
|||||||
? importer.slice(PREFIX.length)
|
? importer.slice(PREFIX.length)
|
||||||
: importer
|
: importer
|
||||||
const resolved = path.resolve(path.dirname(importerNoPrefix), id)
|
const resolved = path.resolve(path.dirname(importerNoPrefix), id)
|
||||||
if (resolvedIds.has(resolved)) { return PREFIX + resolved }
|
if (_modules.has(resolved)) { return PREFIX + resolved }
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
|
|
||||||
load (id) {
|
async load (id) {
|
||||||
if (!id.startsWith(PREFIX)) {
|
if (!id.startsWith(PREFIX)) { return null }
|
||||||
return null
|
|
||||||
}
|
|
||||||
const idNoPrefix = id.slice(PREFIX.length)
|
const idNoPrefix = id.slice(PREFIX.length)
|
||||||
return idNoPrefix in modules
|
if (!_modules.has(idNoPrefix)) { return null }
|
||||||
? resolveModule(modules[idNoPrefix])
|
|
||||||
: resolveModule(resolvedIds.get(idNoPrefix))
|
let m = _modules.get(idNoPrefix)
|
||||||
|
if (typeof m !== 'string' && typeof m.load === 'function') {
|
||||||
|
m = await m.load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('[virtual]', idNoPrefix, '\n', m)
|
||||||
|
|
||||||
|
return {
|
||||||
|
code: m as string,
|
||||||
|
map: null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user