Skip to content

Commit 944f20f

Browse files
fix(create): remove memfs runtime dependency (#501)
1 parent db108a1 commit 944f20f

6 files changed

Lines changed: 78 additions & 91 deletions

File tree

.changeset/quiet-files-travel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/create': patch
3+
---
4+
5+
Remove `memfs` from the published runtime dependencies by reusing the internal in-memory environment.

packages/create/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@
7373
"ejs": "^3.1.10",
7474
"execa": "^9.5.2",
7575
"ignore": "^7.0.3",
76-
"memfs": "^4.17.0",
7776
"parse-gitignore": "^2.0.0",
7877
"prettier": "^3.5.0",
7978
"rimraf": "^6.0.1",
@@ -85,6 +84,7 @@
8584
"@types/parse-gitignore": "^1.0.2",
8685
"@vitest/coverage-v8": "4.1.5",
8786
"eslint": "^9.20.0",
87+
"memfs": "4.17.0",
8888
"typescript": "^6.0.2",
8989
"vitest": "^4.1.5",
9090
"vitest-fetch-mock": "^0.4.5"

packages/create/src/environment.ts

Lines changed: 39 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,16 @@ import {
88
writeFile,
99
} from 'node:fs/promises'
1010
import { existsSync, statSync } from 'node:fs'
11-
import { dirname } from 'node:path'
11+
import { dirname, resolve } from 'node:path'
1212
import { execa } from 'execa'
13-
import { memfs } from 'memfs'
1413
import { rimraf } from 'rimraf'
1514

16-
import {
17-
cleanUpFileArray,
18-
cleanUpFiles,
19-
getBinaryFile,
20-
} from './file-helpers.js'
15+
import { createMemoryEnvironment as createEdgeMemoryEnvironment } from './edge-environment.js'
16+
import { getBinaryFile } from './file-helpers.js'
2117

2218
import type { Environment } from './types.js'
2319

24-
export interface MemoryEnvironmentOutput {
25-
files: Record<string, string>
26-
deletedFiles: Array<string>
27-
commands: Array<{ command: string; args: Array<string> }>
28-
}
20+
export type { MemoryEnvironmentOutput } from './edge-environment.js'
2921

3022
export function createDefaultEnvironment(): Environment {
3123
let errors: Array<string> = []
@@ -115,79 +107,43 @@ export function createDefaultEnvironment(): Environment {
115107
}
116108

117109
export function createMemoryEnvironment(returnPathsRelativeTo: string = '') {
118-
const environment = createDefaultEnvironment()
110+
const { environment, output } =
111+
createEdgeMemoryEnvironment(returnPathsRelativeTo)
112+
const resolvePath = (path: string) => resolve(process.cwd(), path)
119113

120-
const output: MemoryEnvironmentOutput = {
121-
files: {},
122-
commands: [],
123-
deletedFiles: [],
124-
}
114+
const appendFile = environment.appendFile
115+
environment.appendFile = (path, contents) =>
116+
appendFile(resolvePath(path), contents)
125117

126-
const { fs, vol } = memfs({})
118+
const copyFile = environment.copyFile
119+
environment.copyFile = (from, to) =>
120+
copyFile(resolvePath(from), resolvePath(to))
127121

128-
environment.appendFile = async (path: string, contents: string) => {
129-
fs.mkdirSync(dirname(path), { recursive: true })
130-
await fs.appendFileSync(path, contents)
131-
}
132-
environment.copyFile = async (from: string, to: string) => {
133-
fs.mkdirSync(dirname(to), { recursive: true })
134-
fs.copyFileSync(from, to)
135-
return Promise.resolve()
136-
}
137-
environment.execute = async (command: string, args: Array<string>) => {
138-
output.commands.push({
139-
command,
140-
args,
141-
})
142-
return Promise.resolve({ stdout: '' })
143-
}
144-
environment.readFile = async (path: string) => {
145-
return Promise.resolve(fs.readFileSync(path, 'utf-8').toString())
146-
}
147-
environment.writeFile = async (path: string, contents: string) => {
148-
fs.mkdirSync(dirname(path), { recursive: true })
149-
await fs.writeFileSync(path, contents)
150-
}
151-
environment.writeFileBase64 = async (path: string, contents: string) => {
152-
// For the in-memory file system, we are not converting the base64 to binary
153-
// because it's not needed.
154-
fs.mkdirSync(dirname(path), { recursive: true })
155-
await fs.writeFileSync(path, contents)
156-
}
157-
environment.deleteFile = async (path: string) => {
158-
output.deletedFiles.push(path)
159-
if (fs.existsSync(path)) {
160-
await fs.unlinkSync(path)
161-
}
162-
}
163-
environment.finishRun = () => {
164-
output.files = vol.toJSON() as Record<string, string>
165-
for (const file of Object.keys(output.files)) {
166-
if (fs.statSync(file).isDirectory()) {
167-
delete output.files[file]
168-
}
169-
}
170-
if (returnPathsRelativeTo.length) {
171-
output.files = cleanUpFiles(output.files, returnPathsRelativeTo)
172-
output.deletedFiles = cleanUpFileArray(
173-
output.deletedFiles,
174-
returnPathsRelativeTo,
175-
)
176-
}
177-
}
178-
environment.exists = (path: string) => {
179-
return fs.existsSync(path)
180-
}
181-
environment.isDirectory = (path: string) => {
182-
return fs.statSync(path).isDirectory()
183-
}
184-
environment.readdir = async (path: string) => {
185-
return Promise.resolve(fs.readdirSync(path).map((d) => d.toString()))
186-
}
187-
environment.rimraf = async () => {}
122+
const writeFile = environment.writeFile
123+
environment.writeFile = (path, contents) =>
124+
writeFile(resolvePath(path), contents)
188125

189-
return {
190-
environment,
191-
output,
192-
}
126+
const writeFileBase64 = environment.writeFileBase64
127+
environment.writeFileBase64 = (path, contents) =>
128+
writeFileBase64(resolvePath(path), contents)
129+
130+
const deleteFile = environment.deleteFile
131+
environment.deleteFile = (path) => deleteFile(resolvePath(path))
132+
133+
const readFile = environment.readFile
134+
environment.readFile = (path) => readFile(resolvePath(path))
135+
136+
const exists = environment.exists
137+
environment.exists = (path) => exists(resolvePath(path))
138+
139+
const isDirectory = environment.isDirectory
140+
environment.isDirectory = (path) => isDirectory(resolvePath(path))
141+
142+
const readdir = environment.readdir
143+
environment.readdir = (path) => readdir(resolvePath(path))
144+
145+
const rimraf = environment.rimraf
146+
environment.rimraf = (path) => rimraf(resolvePath(path))
147+
148+
return { environment, output }
193149
}

packages/create/src/file-helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export function toCleanPath(absolutePath: string, baseDir: string): string {
4848
if (normalizedPath.startsWith(normalizedBase)) {
4949
cleanPath = normalizedPath.slice(normalizedBase.length)
5050
} else if (hasDrive(normalizedPath) !== hasDrive(normalizedBase)) {
51-
// Handle paths that are missing the Windows drive letter (e.g. memfs on Windows)
51+
// Handle paths that are missing the Windows drive letter in memory.
5252
const pathNoDrive = stripDrive(normalizedPath)
5353
const baseNoDrive = stripDrive(normalizedBase)
5454
if (pathNoDrive.startsWith(baseNoDrive)) {
Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
1-
import { describe, expect, it } from 'vitest'
1+
import { readFile } from 'node:fs/promises'
2+
import { afterEach, describe, expect, it, vi } from 'vitest'
23

3-
import { createApp } from '../src/index.js'
4+
afterEach(() => {
5+
vi.doUnmock('memfs')
6+
vi.resetModules()
7+
})
48

59
describe('index', () => {
6-
it('should be a test', () => {
10+
it('exports createApp', async () => {
11+
const { createApp } = await import('../src/index.js')
12+
13+
expect(createApp).toBeDefined()
14+
})
15+
16+
it('does not import the test-only memory filesystem', async () => {
17+
vi.resetModules()
18+
vi.doMock('memfs', () => {
19+
throw new Error('memfs is unavailable')
20+
})
21+
22+
const { createApp } = await import('../src/index.js')
23+
724
expect(createApp).toBeDefined()
825
})
26+
27+
it('does not publish the test-only memory filesystem', async () => {
28+
const packageJSON = JSON.parse(
29+
await readFile(new URL('../package.json', import.meta.url), 'utf8'),
30+
)
31+
32+
expect(packageJSON.dependencies).not.toHaveProperty('memfs')
33+
expect(packageJSON.devDependencies).toHaveProperty('memfs')
34+
})
935
})

pnpm-lock.yaml

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)