optimization: avoid some double computation
Massimo Melina committed
May 7, 2023 at 20:43 UTC
e6af026b4abbf1a455e3e7a8c90664254278f0d4
1 file changed
+21
-8
src/vfs.ts
+21
-8
@@ -2,7 +2,7 @@
2
3
import fs from 'fs/promises'
4
import { basename, dirname, join, resolve } from 'path'
5
-import { dirStream, dirTraversal, enforceFinal, getOrSet, isDirectory, typedKeys, makeMatcher } from './misc'
5
+import { dirStream, dirTraversal, enforceFinal, getOrSet, isDirectory, typedKeys, makeMatcher, setHidden } from './misc'
6
import Koa from 'koa'
7
import _ from 'lodash'
8
import { defineConfig, setConfig } from './config'
@@ -44,6 +44,7 @@ export interface VfsNode extends Partial<VfsPerm> {
44
isTemp?: true // this node doesn't belong to the tree and was created by necessity
45
original?: VfsNode // if this is a temp node but reflecting an existing node
46
parent?: VfsNode // available when original is available
47
+ isFolder?: boolean
48
}
49
50
export const defaultPerms: VfsPerm = {
@@ -83,6 +84,7 @@ export function isSameFilenameAs(name: string) {
84
85
export function applyParentToChild(child: VfsNode | undefined, parent: VfsNode, name?: string) {
86
const ret: VfsNode = {
87
+ isFolder: child?.children?.length ? true : undefined, // allow child to overwrite this property
88
...child,
89
original: child,
90
isTemp: true,
@@ -111,10 +113,10 @@ export async function urlToNode(url: string, ctx?: Koa.Context, parent: VfsNode=
113
}
114
// does the tree node have a child that goes by this name?
115
const child = parent.children?.find(isSameFilenameAs(name))
114
- if (!child && !parent.source) return // on tree or on disk
116
+ if (!child && !parent.source) return // on tree or on disk, or it doesn't exist
117
118
const ret = applyParentToChild(child, parent, name)
117
- if (child) // yes
119
+ if (child)
120
return urlToNode(rest, ctx, ret, getRest)
121
let onDisk = name
122
if (parent.rename) { // reverse the mapping
@@ -131,7 +133,10 @@ export async function urlToNode(url: string, ctx?: Koa.Context, parent: VfsNode=
133
if (rest)
134
return urlToNode(rest, ctx, ret, getRest)
135
if (ret.source)
134
- try { await fs.stat(ret.source) } // check existence
136
+ try {
137
+ const st = await fs.stat(ret.source) // check existence
138
+ ret.isFolder = st.isDirectory()
139
+ }
140
catch {
141
if (!getRest)
142
return
@@ -166,7 +171,14 @@ export function getNodeName(node: VfsNode) {
171
}
172
173
export async function nodeIsDirectory(node: VfsNode) {
169
- return Boolean(!node.source || await isDirectory(node.source))
174
+ if (node.isFolder !== undefined)
175
+ return node.isFolder
176
+ const isFolder = Boolean(node.children?.length || !node.source || await isDirectory(node.source))
177
+ if (node.isTemp)
178
+ node.isFolder = isFolder
179
+ else
180
+ setHidden(node, { isFolder }) // don't make it to the storage
181
+ return isFolder
182
}
183
184
export function hasPermission(node: VfsNode, perm: keyof VfsPerm, ctx: Koa.Context): boolean {
@@ -236,7 +248,7 @@ export async function* walkNode(parent:VfsNode, ctx?: Koa.Context, depth:number=
248
const map = new Map()
249
map.set(lastDir, parent)
250
// it's important to keep using dirStream in deep-mode, as it is manyfold faster (it parallelizes)
239
- for await (const [path, isDir] of dirStream(source, depth)) {
251
+ for await (const [path, isFolder] of dirStream(source, depth)) {
252
if (ctx?.req.aborted)
253
return
254
const name = prefixPath + (parent.rename?.[path] || path)
@@ -247,13 +259,14 @@ export async function* walkNode(parent:VfsNode, ctx?: Koa.Context, depth:number=
259
parent = map.get(lastDir = dir)
260
}
261
250
- const item = {
262
+ const item: VfsNode = {
263
name,
264
+ isFolder,
265
source: join(source, path),
266
rename: renameUnderPath(parent.rename, path),
267
}
268
if (!canSee(item)) continue
256
- if (isDir)
269
+ if (isFolder)
270
map.set(name, item)
271
yield item
272
}