From 9a8d992dbb710f3103d5a5ce83f52c2272e1fd8e Mon Sep 17 00:00:00 2001 From: pooya parsa Date: Wed, 8 Sep 2021 22:01:21 +0200 Subject: [PATCH] feat(nitro): assets driver (#511) --- src/package.json | 2 +- src/rollup/plugins/assets.ts | 116 ++++++++++++++++++---------------- src/rollup/plugins/storage.ts | 4 ++ src/types/shims.d.ts | 10 +-- 4 files changed, 71 insertions(+), 61 deletions(-) diff --git a/src/package.json b/src/package.json index 28c4d07f4c..27ceebbb4b 100644 --- a/src/package.json +++ b/src/package.json @@ -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", diff --git a/src/rollup/plugins/assets.ts b/src/rollup/plugins/assets.ts index 43b08100f1..cd7071d1ad 100644 --- a/src/rollup/plugins/assets.ts +++ b/src/rollup/plugins/assets.ts @@ -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 { @@ -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 = {} for (const assetdir in opts.dirs) { const dirOpts = opts.dirs[assetdir] @@ -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) { + 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 : {} + } +} +` +} diff --git a/src/rollup/plugins/storage.ts b/src/rollup/plugins/storage.ts index 16bff72745..8531c5a2e0 100644 --- a/src/rollup/plugins/storage.ts +++ b/src/rollup/plugins/storage.ts @@ -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')} ` }) diff --git a/src/types/shims.d.ts b/src/types/shims.d.ts index 613868b078..baff617ec2 100644 --- a/src/types/shims.d.ts +++ b/src/types/shims.d.ts @@ -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 { @@ -12,6 +11,7 @@ declare global { } declare module '#storage' { + import type { Storage } from 'unstorage' export const storage: Storage } @@ -19,5 +19,7 @@ declare module '#assets' { export interface AssetMeta { type?: string, etag?: string, mtime?: string } export function readAsset(id: string): Promise export function statAsset(id: string): Promise - export function getAsset(id: string): { read: () => Promise, meta: AssetMeta } + export function getKeys() : Promise } + +export default {}