Skip to content

Commit

Permalink
add placeholder support to memfs implementation
Browse files Browse the repository at this point in the history
[ghstack-poisoned]
  • Loading branch information
janfjohannes committed Mar 20, 2024
1 parent 2a815b2 commit dadc026
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 6 deletions.
7 changes: 7 additions & 0 deletions .changeset/lemon-boxes-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@lix-js/client": minor
"@lix-js/server": minor
"@lix-js/fs": minor
---

release for lazy checkout
8 changes: 7 additions & 1 deletion lix/source-code/client/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ type Args = {
nodeishFs: NodeishFilesystem
verbose?: boolean
description?: string
intercept?: (args: { prop: keyof NodeishFilesystem; execute: () => any }) => any
intercept?: (args: {
prop: keyof NodeishFilesystem
execute: () => any
argumentsList: any[]
}) => any
}

export const withLazyFetching = ({
nodeishFs,
verbose = false,
Expand All @@ -31,6 +36,7 @@ export const withLazyFetching = ({
? intercept({ prop, argumentsList, execute } as {
prop: keyof typeof nodeishFs
execute: () => any
argumentsList: any[]
})
: execute()
},
Expand Down
35 changes: 35 additions & 0 deletions lix/source-code/fs/src/implementations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,41 @@ const runFsTestSuite = async (
])
})

test.skipIf(isNodeFs)("placeholders", async () => {
const placeholderPath = `/placeholders/subdir`

await fs.mkdir(placeholderPath, { recursive: true })

await fs.writeFile(`${placeholderPath}/file`, "")

fs._createPlaceholder(placeholderPath + "/test")

expect(fs._isPlaceholder(placeholderPath + "/test")).toBe(true)
expect(fs._isPlaceholder(placeholderPath + "/noexists")).toBe(false)
expect(fs._isPlaceholder(placeholderPath + "/file")).toBe(false)

const dirents = await fs.readdir(`/placeholders/subdir`)

expect(dirents).toStrictEqual(["file", "test"])

await expect(async () => await fs.readFile(`${placeholderPath}/test`)).rejects.toThrow(
/EPLACEHOLDER/
)

await fs.rm("/placeholders", { recursive: true })

const finalDirents = await fs.readdir(`/`)

expect(finalDirents).toStrictEqual([
"home",
"file2",
"file3",
"file1.link",
"file3.link",
"user1.link",
])
})

test("unlink", async () => {
await fs.unlink(`${tempDir}/user1.link`)
await fs.unlink(`${tempDir}/file1.link`)
Expand Down
44 changes: 41 additions & 3 deletions lix/source-code/fs/src/memoryFs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { NodeishFilesystem, NodeishStats, FileChangeInfo } from "./NodeishF
import { FilesystemError } from "./errors/FilesystemError.js"
import { normalPath, getBasename, getDirname } from "./utilities/helpers.js"

type Inode = Uint8Array | Set<string>
type Inode = Uint8Array | Set<string> | { placeholder: true }

export type Snapshot = {
fsMap: {
[key: string]: string[] | string
[key: string]: string[] | string | { placeholder: true }
}
fsStats: {
[key: string]: {
Expand Down Expand Up @@ -148,6 +148,31 @@ export function createNodeishMemoryFs(): NodeishFilesystem {

return {
_state: state,

_createPlaceholder: function (
path: Parameters<NodeishFilesystem["writeFile"]>[0],
options?: Parameters<NodeishFilesystem["writeFile"]>[2]
) {
path = normalPath(path)
const dirName = getDirname(path)
const baseName = getBasename(path)
const parentDir: Inode | undefined = state.fsMap.get(dirName)
if (!(parentDir instanceof Set)) throw new FilesystemError("ENOENT", path, "writeFile")
parentDir.add(baseName)
newStatEntry(path, state.fsStats, 0, options?.mode ?? 0o644)
state.fsMap.set(path, { placeholder: true })
},

_isPlaceholder: function (path: Parameters<NodeishFilesystem["writeFile"]>[0]) {
path = normalPath(path)
const entry = state.fsMap.get(path)

if (entry && "placeholder" in entry) {
return true
}
return false
},

writeFile: async function (
path: Parameters<NodeishFilesystem["writeFile"]>[0],
data: Parameters<NodeishFilesystem["writeFile"]>[1],
Expand Down Expand Up @@ -194,6 +219,7 @@ export function createNodeishMemoryFs(): NodeishFilesystem {

if (file instanceof Set) throw new FilesystemError("EISDIR", path, "readFile")
if (file === undefined) throw new FilesystemError("ENOENT", path, "readFile")
if ("placeholder" in file) throw new FilesystemError("EPLACEHOLDER", path, "readFile")
if (!(options?.encoding || typeof options === "string")) return file

return decoder.decode(file)
Expand Down Expand Up @@ -257,8 +283,9 @@ export function createNodeishMemoryFs(): NodeishFilesystem {
throw new FilesystemError("ENOENT", path, "rm")

if (parentDir instanceof Uint8Array) throw new FilesystemError("ENOTDIR", path, "rm")
if ("placeholder" in parentDir) throw new FilesystemError("EPLACEHOLDER", path, "readFile")

if (target instanceof Uint8Array) {
if (target instanceof Uint8Array || "placeholder" in target) {
parentDir.delete(baseName)
state.fsStats.delete(path)
state.fsMap.delete(path)
Expand Down Expand Up @@ -392,6 +419,10 @@ export function createNodeishMemoryFs(): NodeishFilesystem {
if (parentDir instanceof Uint8Array || target instanceof Uint8Array)
throw new FilesystemError("ENOTDIR", path, "rmdir")

if ("placeholder" in parentDir || "placeholder" in target) {
throw new FilesystemError("EPLACEHOLDER", path, "readFile")
}

if (target.size) throw new FilesystemError("ENOTEMPTY", path, "rmdir")

parentDir.delete(baseName)
Expand Down Expand Up @@ -427,6 +458,9 @@ export function createNodeishMemoryFs(): NodeishFilesystem {
if (targetInode !== undefined) {
state.fsMap.set(path, targetInode)
}
if ("placeholder" in parentDir) {
throw new FilesystemError("EPLACEHOLDER", path, "readFile")
}

parentDir.add(getBasename(path))
newStatEntry(path, state.fsStats, 2, 0o777, target)
Expand All @@ -449,6 +483,10 @@ export function createNodeishMemoryFs(): NodeishFilesystem {
throw new FilesystemError("EISDIR", path, "unlink")
}

if ("placeholder" in parentDir || "placeholder" in target) {
throw new FilesystemError("EPLACEHOLDER", path, "readFile")
}

parentDir.delete(getBasename(path))
state.fsStats.delete(path)
state.fsMap.delete(path)
Expand Down
4 changes: 2 additions & 2 deletions lix/source-code/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "@lix-js/server",
"type": "module",
"version": "0.0.1",
"private": true,
"exports": {
".": "./dist/index.js"
Expand Down Expand Up @@ -39,6 +40,5 @@
"typescript": "5.2.2",
"vitest": "0.34.3"
},
"license": "Apache-2.0",
"version": null
"license": "Apache-2.0"
}

0 comments on commit dadc026

Please sign in to comment.