Skip to content

Commit

Permalink
feat(nitro): assets driver (#511)
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 authored Sep 8, 2021
1 parent fcafa12 commit 9a8d992
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 61 deletions.
2 changes: 1 addition & 1 deletion src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"table": "^6.7.1",
"ufo": "^0.7.9",
"unenv": "^0.3.3",
"unstorage": "^0.2.3",
"unstorage": "^0.2.5",
"upath": "^2.0.1",
"vue": "3.2.10",
"vue-bundle-renderer": "^0.3.0",
Expand Down
116 changes: 60 additions & 56 deletions src/rollup/plugins/assets.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { promises as fsp } from 'fs'
import type { Plugin } from 'rollup'
import createEtag from 'etag'
import mime from 'mime'
import { resolve } from 'upath'
import globby from 'globby'
import type { Plugin } from 'rollup'
import virtual from './virtual'

export interface AssetOptions {
Expand All @@ -16,60 +16,26 @@ export interface AssetOptions {
}
}

export function assets (opts: AssetOptions): Plugin {
type Asset = {
fsPath: string,
meta: {
type?: string,
etag?: string,
mtime?: string
}
interface 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
}
`

export function assets (opts: AssetOptions): Plugin {
if (!opts.inline) {
return virtual({
'#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)
}
`
})
// Development: Use filesystem
return virtual({ '#assets': getAssetsDev(opts.dirs) })
}

// Production: Bundle assets
return virtual({
'#assets': {
async load () {
// Scan all assets
const assets: Record<string, Asset> = {}
for (const assetdir in opts.dirs) {
const dirOpts = opts.dirs[assetdir]
Expand All @@ -88,17 +54,55 @@ export function getAsset (id) {
}
}
}
const inlineAssets = `const assets = {\n${Object.keys(assets).map(id =>
` ['${id}']: {\n read: () => import('${assets[id].fsPath}').then(r => r.default || r),\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]
}`
return getAssetProd(assets)
}
}
})
}

function getAssetsDev (dirs) {
return `
import { createStorage } from 'unstorage'
import fsDriver from 'unstorage/drivers/fs'
const dirs = ${JSON.stringify(dirs)}
export const assets = createStorage()
for (const [dirname, dirOpts] of Object.entries(dirs)) {
assets.mount(dirname, fsDriver({ base: dirOpts.dir }))
}
`
}

function normalizeKey (key) {
return key.replace(/[/\\\\]/g, ':').replace(/^:|:$/g, '')
}

function getAssetProd (assets: Record<string, Asset>) {
return `
const _assets = {\n${Object.entries(assets).map(([id, asset]) =>
` ['${normalizeKey(id)}']: {\n import: () => import('${asset.fsPath}').then(r => r.default || r),\n meta: ${JSON.stringify(asset.meta)}\n }`
).join(',\n')}\n}
${normalizeKey.toString()}
export const assets = {
getKeys() {
return Object.keys(_assets)
},
hasItem (id) {
id = normalizeKey(id)
return id in _assets
},
getItem (id) {
id = normalizeKey(id)
return _assets[id] ? _assets[id].import() : null
},
getMeta (id) {
id = normalizeKey(id)
return _assets[id] ? _assets[id].meta : {}
}
}
`
}
4 changes: 4 additions & 0 deletions src/rollup/plugins/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@ export function storage (opts: StorageOptions) {
return virtual({
'#storage': `
import { createStorage } from 'unstorage'
import { assets } from '#assets'
${driverImports.map(i => `import ${getImportName(i)} from '${i}'`).join('\n')}
export const storage = createStorage({})
storage.mount('/assets', assets)
${mounts.map(m => `storage.mount('${m.path}', ${getImportName(m.driver)}(${JSON.stringify(m.opts)}))`).join('\n')}
`
})
Expand Down
10 changes: 6 additions & 4 deletions src/types/shims.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { $Fetch } from 'ohmyfetch'
import type { Storage } from 'unstorage'

declare global {
import type { $Fetch } from 'ohmyfetch'

// eslint-disable-next-line no-var
var $fetch: $Fetch
namespace NodeJS {
Expand All @@ -12,12 +11,15 @@ declare global {
}

declare module '#storage' {
import type { Storage } from 'unstorage'
export const storage: Storage
}

declare module '#assets' {
export interface AssetMeta { type?: string, etag?: string, mtime?: string }
export function readAsset<T=any>(id: string): Promise<T>
export function statAsset(id: string): Promise<AssetMeta>
export function getAsset<T=any>(id: string): { read: () => Promise<T>, meta: AssetMeta }
export function getKeys() : Promise<string[]>
}

export default {}

0 comments on commit 9a8d992

Please sign in to comment.