Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quiet-files-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/create': patch
---

Remove `memfs` from the published runtime dependencies by reusing the internal in-memory environment.
2 changes: 1 addition & 1 deletion packages/create/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
"ejs": "^3.1.10",
"execa": "^9.5.2",
"ignore": "^7.0.3",
"memfs": "^4.17.0",
"parse-gitignore": "^2.0.0",
"prettier": "^3.5.0",
"rimraf": "^6.0.1",
Expand All @@ -85,6 +84,7 @@
"@types/parse-gitignore": "^1.0.2",
"@vitest/coverage-v8": "4.1.5",
"eslint": "^9.20.0",
"memfs": "4.17.0",
"typescript": "^6.0.2",
"vitest": "^4.1.5",
"vitest-fetch-mock": "^0.4.5"
Expand Down
122 changes: 39 additions & 83 deletions packages/create/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,16 @@ import {
writeFile,
} from 'node:fs/promises'
import { existsSync, statSync } from 'node:fs'
import { dirname } from 'node:path'
import { dirname, resolve } from 'node:path'
import { execa } from 'execa'
import { memfs } from 'memfs'
import { rimraf } from 'rimraf'

import {
cleanUpFileArray,
cleanUpFiles,
getBinaryFile,
} from './file-helpers.js'
import { createMemoryEnvironment as createEdgeMemoryEnvironment } from './edge-environment.js'
import { getBinaryFile } from './file-helpers.js'

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

export interface MemoryEnvironmentOutput {
files: Record<string, string>
deletedFiles: Array<string>
commands: Array<{ command: string; args: Array<string> }>
}
export type { MemoryEnvironmentOutput } from './edge-environment.js'

export function createDefaultEnvironment(): Environment {
let errors: Array<string> = []
Expand Down Expand Up @@ -115,79 +107,43 @@ export function createDefaultEnvironment(): Environment {
}

export function createMemoryEnvironment(returnPathsRelativeTo: string = '') {
const environment = createDefaultEnvironment()
const { environment, output } =
createEdgeMemoryEnvironment(returnPathsRelativeTo)
const resolvePath = (path: string) => resolve(process.cwd(), path)

const output: MemoryEnvironmentOutput = {
files: {},
commands: [],
deletedFiles: [],
}
const appendFile = environment.appendFile
environment.appendFile = (path, contents) =>
appendFile(resolvePath(path), contents)

const { fs, vol } = memfs({})
const copyFile = environment.copyFile
environment.copyFile = (from, to) =>
copyFile(resolvePath(from), resolvePath(to))

environment.appendFile = async (path: string, contents: string) => {
fs.mkdirSync(dirname(path), { recursive: true })
await fs.appendFileSync(path, contents)
}
environment.copyFile = async (from: string, to: string) => {
fs.mkdirSync(dirname(to), { recursive: true })
fs.copyFileSync(from, to)
return Promise.resolve()
}
environment.execute = async (command: string, args: Array<string>) => {
output.commands.push({
command,
args,
})
return Promise.resolve({ stdout: '' })
}
environment.readFile = async (path: string) => {
return Promise.resolve(fs.readFileSync(path, 'utf-8').toString())
}
environment.writeFile = async (path: string, contents: string) => {
fs.mkdirSync(dirname(path), { recursive: true })
await fs.writeFileSync(path, contents)
}
environment.writeFileBase64 = async (path: string, contents: string) => {
// For the in-memory file system, we are not converting the base64 to binary
// because it's not needed.
fs.mkdirSync(dirname(path), { recursive: true })
await fs.writeFileSync(path, contents)
}
environment.deleteFile = async (path: string) => {
output.deletedFiles.push(path)
if (fs.existsSync(path)) {
await fs.unlinkSync(path)
}
}
environment.finishRun = () => {
output.files = vol.toJSON() as Record<string, string>
for (const file of Object.keys(output.files)) {
if (fs.statSync(file).isDirectory()) {
delete output.files[file]
}
}
if (returnPathsRelativeTo.length) {
output.files = cleanUpFiles(output.files, returnPathsRelativeTo)
output.deletedFiles = cleanUpFileArray(
output.deletedFiles,
returnPathsRelativeTo,
)
}
}
environment.exists = (path: string) => {
return fs.existsSync(path)
}
environment.isDirectory = (path: string) => {
return fs.statSync(path).isDirectory()
}
environment.readdir = async (path: string) => {
return Promise.resolve(fs.readdirSync(path).map((d) => d.toString()))
}
environment.rimraf = async () => {}
const writeFile = environment.writeFile
environment.writeFile = (path, contents) =>
writeFile(resolvePath(path), contents)

return {
environment,
output,
}
const writeFileBase64 = environment.writeFileBase64
environment.writeFileBase64 = (path, contents) =>
writeFileBase64(resolvePath(path), contents)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const deleteFile = environment.deleteFile
environment.deleteFile = (path) => deleteFile(resolvePath(path))

const readFile = environment.readFile
environment.readFile = (path) => readFile(resolvePath(path))

const exists = environment.exists
environment.exists = (path) => exists(resolvePath(path))

const isDirectory = environment.isDirectory
environment.isDirectory = (path) => isDirectory(resolvePath(path))

const readdir = environment.readdir
environment.readdir = (path) => readdir(resolvePath(path))

const rimraf = environment.rimraf
environment.rimraf = (path) => rimraf(resolvePath(path))

return { environment, output }
}
2 changes: 1 addition & 1 deletion packages/create/src/file-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function toCleanPath(absolutePath: string, baseDir: string): string {
if (normalizedPath.startsWith(normalizedBase)) {
cleanPath = normalizedPath.slice(normalizedBase.length)
} else if (hasDrive(normalizedPath) !== hasDrive(normalizedBase)) {
// Handle paths that are missing the Windows drive letter (e.g. memfs on Windows)
// Handle paths that are missing the Windows drive letter in memory.
const pathNoDrive = stripDrive(normalizedPath)
const baseNoDrive = stripDrive(normalizedBase)
if (pathNoDrive.startsWith(baseNoDrive)) {
Expand Down
32 changes: 29 additions & 3 deletions packages/create/tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
import { describe, expect, it } from 'vitest'
import { readFile } from 'node:fs/promises'
import { afterEach, describe, expect, it, vi } from 'vitest'

import { createApp } from '../src/index.js'
afterEach(() => {
vi.doUnmock('memfs')
vi.resetModules()
})

describe('index', () => {
it('should be a test', () => {
it('exports createApp', async () => {
const { createApp } = await import('../src/index.js')

expect(createApp).toBeDefined()
})

it('does not import the test-only memory filesystem', async () => {
vi.resetModules()
vi.doMock('memfs', () => {
throw new Error('memfs is unavailable')
})

const { createApp } = await import('../src/index.js')

expect(createApp).toBeDefined()
})

it('does not publish the test-only memory filesystem', async () => {
const packageJSON = JSON.parse(
await readFile(new URL('../package.json', import.meta.url), 'utf8'),
)

expect(packageJSON.dependencies).not.toHaveProperty('memfs')
expect(packageJSON.devDependencies).toHaveProperty('memfs')
})
})
6 changes: 3 additions & 3 deletions pnpm-lock.yaml

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

Loading