ipfs-unixfs-importer
Version:
JavaScript implementation of the UnixFs importer used by IPFS
124 lines (100 loc) • 3.26 kB
text/typescript
import { DirFlat } from './dir-flat.js'
import { flatToShard } from './flat-to-shard.js'
import { Dir } from './dir.js'
import { toPathComponents } from './utils/to-path-components.js'
import type { ImportResult, InProgressImportResult, TreeBuilder, WritableStorage } from './index.js'
import type { PersistOptions } from './utils/persist.js'
export interface AddToTreeOptions extends PersistOptions {
shardSplitThresholdBytes: number
}
async function addToTree (elem: InProgressImportResult, tree: Dir, options: AddToTreeOptions): Promise<Dir> {
const pathElems = toPathComponents(elem.path ?? '')
const lastIndex = pathElems.length - 1
let parent = tree
let currentPath = ''
for (let i = 0; i < pathElems.length; i++) {
const pathElem = pathElems[i]
currentPath += `${currentPath !== '' ? '/' : ''}${pathElem}`
const last = (i === lastIndex)
parent.dirty = true
parent.cid = undefined
parent.size = undefined
if (last) {
await parent.put(pathElem, elem)
tree = await flatToShard(null, parent, options.shardSplitThresholdBytes, options)
} else {
let dir = await parent.get(pathElem)
if ((dir == null) || !(dir instanceof Dir)) {
dir = new DirFlat({
root: false,
dir: true,
parent,
parentKey: pathElem,
path: currentPath,
dirty: true,
flat: true,
mtime: dir?.unixfs?.mtime,
mode: dir?.unixfs?.mode
}, options)
}
await parent.put(pathElem, dir)
parent = dir
}
}
return tree
}
async function * flushAndYield (tree: Dir | InProgressImportResult, blockstore: WritableStorage): AsyncGenerator<ImportResult> {
if (!(tree instanceof Dir)) {
if (tree.unixfs?.isDirectory() === true) {
yield tree
}
return
}
yield * tree.flush(blockstore)
}
export interface TreeBuilderOptions extends AddToTreeOptions {
wrapWithDirectory: boolean
}
export function defaultTreeBuilder (options: TreeBuilderOptions): TreeBuilder {
return async function * treeBuilder (source, block) {
let tree: Dir = new DirFlat({
root: true,
dir: true,
path: '',
dirty: true,
flat: true
}, options)
let rootDir: string | undefined
let singleRoot = false
for await (const entry of source) {
if (entry == null) {
continue
}
// if all paths are from the same root directory, we should
// wrap them all in that root directory
const dir = `${entry.originalPath ?? ''}`.split('/')[0]
if (dir != null && dir !== '') {
if (rootDir == null) {
rootDir = dir
singleRoot = true
} else if (rootDir !== dir) {
singleRoot = false
}
}
tree = await addToTree(entry, tree, options)
if (entry.unixfs == null || !entry.unixfs.isDirectory()) {
yield entry
}
}
if (options.wrapWithDirectory || (singleRoot && tree.childCount() > 1)) {
yield * flushAndYield(tree, block)
} else {
for await (const unwrapped of tree.eachChildSeries()) {
if (unwrapped == null) {
continue
}
yield * flushAndYield(unwrapped.child, block)
}
}
}
}