Skip to content

Commit

Permalink
feat(assets): support for layers (#618)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
arashsheyda and antfu committed Mar 20, 2024
1 parent bee12e8 commit b8572b6
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 31 deletions.
1 change: 1 addition & 0 deletions packages/devtools-kit/src/_types/integrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ export interface AssetInfo {
filePath: string
size: number
mtime: number
layer?: string
}

export interface AssetEntry {
Expand Down
3 changes: 2 additions & 1 deletion packages/devtools/client/components/AssetGridItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const path = computed(() => {
</script>

<template>
<button flex="~ col gap-1" hover="bg-active" items-center of-hidden rounded p2>
<button relative flex="~ col gap-1" hover="bg-active" items-center of-hidden rounded p2>
<NIcon v-if="asset.layer" icon="i-carbon-layers" absolute right-4 top-4 bg-primary />
<AssetPreview h-30 w-30 rounded border="~ base" :asset="asset" />
<div w-full of-hidden truncate ws-nowrap text-center text-xs>
{{ path }}
Expand Down
1 change: 1 addition & 0 deletions packages/devtools/client/components/AssetListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const icon = computed(() => {
<div :class="icon" />
<span :class="{ 'flex items-center': isCollection }" flex-auto text-start text-sm font-mono>
{{ item.path }}
<NIcon v-if="item.layer" icon="i-carbon-layers" bg-primary />
</span>
<NIcon v-if="isCollection" icon="carbon:chevron-right" :transform-rotate="open ? 90 : 0" transition />
</button>
Expand Down
77 changes: 47 additions & 30 deletions packages/devtools/src/server-rpc/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export function setupAssetsRPC({ nuxt, ensureDevAuthToken, refresh, options }: N
let cache: AssetInfo[] | null = null

const extensions = options.assets?.uploadExtensions || defaultAllowedExtensions

const publicDir = resolve(nuxt.options.srcDir, nuxt.options.dir.public)
const layerDirs = [publicDir, ...nuxt.options._layers.map(layer => resolve(layer.cwd, 'public'))]

const refreshDebounced = debounce(() => {
cache = null
Expand All @@ -30,39 +30,42 @@ export function setupAssetsRPC({ nuxt, ensureDevAuthToken, refresh, options }: N
return cache

const baseURL = nuxt.options.app.baseURL
const files = await fg(['**/*'], {
cwd: publicDir,
onlyFiles: true,
})

function guessType(path: string): AssetType {
if (/\.(png|jpe?g|jxl|gif|svg|webp|avif|ico|bmp|tiff?)$/i.test(path))
return 'image'
if (/\.(mp4|webm|ogv|mov|avi|flv|wmv|mpg|mpeg|mkv|3gp|3g2|ts|mts|m2ts|vob|ogm|ogx|rm|rmvb|asf|amv|divx|m4v|svi|viv|f4v|f4p|f4a|f4b)$/i.test(path))
return 'video'
if (/\.(mp3|wav|ogg|flac|aac|wma|alac|ape|ac3|dts|tta|opus|amr|aiff|au|mid|midi|ra|rm|wv|weba|dss|spx|vox|tak|dsf|dff|dsd|cda)$/i.test(path))
return 'audio'
if (/\.(woff2?|eot|ttf|otf|ttc|pfa|pfb|pfm|afm)/i.test(path))
return 'font'
if (/\.(json[5c]?|te?xt|[mc]?[jt]sx?|md[cx]?|markdown)/i.test(path))
return 'text'
return 'other'
const dirs: { layerDir: string, files: string[] }[] = []

for (const layerDir of layerDirs) {
const files = await fg(['**/*'], {
cwd: layerDir,
onlyFiles: true,
})
dirs.push({ layerDir, files })
}

cache = await Promise.all(files.map(async (path) => {
const filePath = resolve(publicDir, path)
const stat = await fsp.lstat(filePath)
return {
path,
publicPath: join(baseURL, path),
filePath,
type: guessType(path),
size: stat.size,
mtime: stat.mtimeMs,
const uniquePaths = new Set()
cache = []

for (const { layerDir, files } of dirs) {
for (const path of files) {
const filePath = resolve(layerDir, path)
const stat = await fsp.lstat(filePath)
const fullPath = join(baseURL, path)

// Check if path already exists in uniquePaths set
if (!uniquePaths.has(fullPath)) {
cache.push({
path,
publicPath: fullPath,
filePath,
type: guessType(path),
size: stat.size,
mtime: stat.mtimeMs,
layer: publicDir !== layerDir ? layerDir : undefined,
})
uniquePaths.add(fullPath)
}
}
}))
}

return cache
return cache.sort((a, b) => a.path.localeCompare(b.path))
}

return {
Expand Down Expand Up @@ -147,3 +150,17 @@ export function setupAssetsRPC({ nuxt, ensureDevAuthToken, refresh, options }: N
},
} satisfies Partial<ServerFunctions>
}

function guessType(path: string): AssetType {
if (/\.(png|jpe?g|jxl|gif|svg|webp|avif|ico|bmp|tiff?)$/i.test(path))
return 'image'
if (/\.(mp4|webm|ogv|mov|avi|flv|wmv|mpg|mpeg|mkv|3gp|3g2|ts|mts|m2ts|vob|ogm|ogx|rm|rmvb|asf|amv|divx|m4v|svi|viv|f4v|f4p|f4a|f4b)$/i.test(path))
return 'video'
if (/\.(mp3|wav|ogg|flac|aac|wma|alac|ape|ac3|dts|tta|opus|amr|aiff|au|mid|midi|ra|rm|wv|weba|dss|spx|vox|tak|dsf|dff|dsd|cda)$/i.test(path))
return 'audio'
if (/\.(woff2?|eot|ttf|otf|ttc|pfa|pfb|pfm|afm)/i.test(path))
return 'font'
if (/\.(json[5c]?|te?xt|[mc]?[jt]sx?|md[cx]?|markdown)/i.test(path))
return 'text'
return 'other'
}
9 changes: 9 additions & 0 deletions playgrounds/empty/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{
"name": "empty",
"private": true,
"main": "nuxt.config.ts",
"files": [
"app.vue",
"components",
"nuxt.config.ts",
"pages",
"public"
],
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
Expand Down
1 change: 1 addition & 0 deletions playgrounds/empty/public/empty.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Empty!
2 changes: 2 additions & 0 deletions playgrounds/tab-layers/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false
8 changes: 8 additions & 0 deletions playgrounds/tab-layers/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script setup lang="ts">
</script>

<template>
<div px10 text-6xl>
Welcome!
</div>
</template>
10 changes: 10 additions & 0 deletions playgrounds/tab-layers/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
modules: [
'../../local',
],

extends: [
'../empty',
],
})
14 changes: 14 additions & 0 deletions playgrounds/tab-layers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
"devDependencies": {
"@types/node": "^20.11.27",
"empty": "link:/Users/arash/Developer/projects/Nuxt/devtools/playgrounds/empty",
"nuxt": "^3.10.3"
}
}
4 changes: 4 additions & 0 deletions playgrounds/tab-layers/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b8572b6

Please sign in to comment.