diff --git a/.changeset/calm-apes-double.md b/.changeset/calm-apes-double.md new file mode 100644 index 000000000..620f39ce4 --- /dev/null +++ b/.changeset/calm-apes-double.md @@ -0,0 +1,5 @@ +--- +'@vercel/fs-detectors': major +--- + +`LocalFileSystemDetector#readdir()` now returns paths relative to the root dir, instead of absolute paths. This is to align with the usage of the detectors that are using the `DetectorFilesystem` interface. diff --git a/packages/fs-detectors/package.json b/packages/fs-detectors/package.json index ddb8eaf2f..55f4e8e86 100644 --- a/packages/fs-detectors/package.json +++ b/packages/fs-detectors/package.json @@ -15,8 +15,8 @@ "license": "Apache-2.0", "scripts": { "build": "tsc", - "test": "jest --env node --verbose --runInBand --bail test/unit.*test.*", - "test-unit": "pnpm test" + "test": "jest --env node --verbose --runInBand --bail", + "test-unit": "pnpm test test/unit.*test.*" }, "dependencies": { "@vercel/error-utils": "1.0.10", diff --git a/packages/fs-detectors/src/detectors/local-file-system-detector.ts b/packages/fs-detectors/src/detectors/local-file-system-detector.ts index a9502e25e..272bf448b 100644 --- a/packages/fs-detectors/src/detectors/local-file-system-detector.ts +++ b/packages/fs-detectors/src/detectors/local-file-system-detector.ts @@ -6,10 +6,12 @@ import { isErrnoException } from '@vercel/error-utils'; export class LocalFileSystemDetector extends DetectorFilesystem { private rootPath: string; + constructor(rootPath: string) { super(); this.rootPath = rootPath; } + async _hasPath(name: string): Promise { try { await fs.stat(this.getFilePath(name)); @@ -21,13 +23,16 @@ export class LocalFileSystemDetector extends DetectorFilesystem { throw err; } } + _readFile(name: string): Promise { return fs.readFile(this.getFilePath(name)); } + async _isFile(name: string): Promise { const stat = await fs.stat(this.getFilePath(name)); return stat.isFile(); } + async _readdir(name: string): Promise { const dirPath = this.getFilePath(name); const dir = await fs.readdir(dirPath, { @@ -44,14 +49,22 @@ export class LocalFileSystemDetector extends DetectorFilesystem { }; return dir.map(dirent => ({ name: dirent.name, - path: path.join(dirPath, dirent.name), + path: path.join(this.getRelativeFilePath(name), dirent.name), type: getType(dirent), })); } + _chdir(name: string): DetectorFilesystem { return new LocalFileSystemDetector(this.getFilePath(name)); } + + private getRelativeFilePath(name: string) { + return name.startsWith(this.rootPath) + ? path.relative(this.rootPath, name) + : name; + } + private getFilePath(name: string) { - return path.join(this.rootPath, name); + return path.join(this.rootPath, this.getRelativeFilePath(name)); } } diff --git a/packages/fs-detectors/test/unit.detect-monorepo-managers.test.ts b/packages/fs-detectors/test/unit.detect-monorepo-managers.test.ts index baf81e5f7..c3834f1dc 100644 --- a/packages/fs-detectors/test/unit.detect-monorepo-managers.test.ts +++ b/packages/fs-detectors/test/unit.detect-monorepo-managers.test.ts @@ -1,7 +1,7 @@ import path from 'path'; +import { LocalFileSystemDetector } from '../src'; import { detectFramework } from '../src/detect-framework'; import monorepoManagers from '../src/monorepos/monorepo-managers'; -import { FixtureFilesystem } from './utils/fixture-filesystem'; describe('monorepo-managers', () => { describe.each([ @@ -17,7 +17,7 @@ describe('monorepo-managers', () => { it(testName, async () => { const fixture = path.join(__dirname, 'fixtures', fixturePath); - const fs = new FixtureFilesystem(fixture); + const fs = new LocalFileSystemDetector(fixture); const result = await detectFramework({ fs, diff --git a/packages/fs-detectors/test/unit.detect-package-managers.test.ts b/packages/fs-detectors/test/unit.detect-package-managers.test.ts index 404e01774..100158f6b 100644 --- a/packages/fs-detectors/test/unit.detect-package-managers.test.ts +++ b/packages/fs-detectors/test/unit.detect-package-managers.test.ts @@ -1,6 +1,9 @@ import path from 'path'; -import { packageManagers, detectFramework } from '../src'; -import { FixtureFilesystem } from './utils/fixture-filesystem'; +import { + packageManagers, + detectFramework, + LocalFileSystemDetector, +} from '../src'; describe('package-managers', () => { describe.each([ @@ -16,7 +19,7 @@ describe('package-managers', () => { it(testName, async () => { const fixture = path.join(__dirname, 'fixtures', fixturePath); - const fs = new FixtureFilesystem(fixture); + const fs = new LocalFileSystemDetector(fixture); const result = await detectFramework({ fs, diff --git a/packages/fs-detectors/test/unit.detect-workspace-managers.test.ts b/packages/fs-detectors/test/unit.detect-workspace-managers.test.ts index d634359ad..fdfa12b98 100644 --- a/packages/fs-detectors/test/unit.detect-workspace-managers.test.ts +++ b/packages/fs-detectors/test/unit.detect-workspace-managers.test.ts @@ -1,7 +1,7 @@ import path from 'path'; +import { LocalFileSystemDetector } from '../src'; import { detectFramework } from '../src/detect-framework'; import workspaceManagers from '../src/workspaces/workspace-managers'; -import { FixtureFilesystem } from './utils/fixture-filesystem'; describe('workspace-managers', () => { describe.each([ @@ -19,7 +19,7 @@ describe('workspace-managers', () => { it(testName, async () => { const fixture = path.join(__dirname, 'fixtures', fixturePath); - const fs = new FixtureFilesystem(fixture); + const fs = new LocalFileSystemDetector(fixture); const result = await detectFramework({ fs, diff --git a/packages/fs-detectors/test/unit.examples.test.ts b/packages/fs-detectors/test/unit.examples.test.ts index 1b52b2dc3..f1669b1db 100644 --- a/packages/fs-detectors/test/unit.examples.test.ts +++ b/packages/fs-detectors/test/unit.examples.test.ts @@ -1,13 +1,12 @@ import frameworkList from '@vercel/frameworks'; -import { detectFramework } from '../src'; -import { FixtureFilesystem } from './utils/fixture-filesystem'; +import { detectFramework, LocalFileSystemDetector } from '../src'; import { getExamples } from '../../../examples/__tests__/test-utils'; describe('examples should be detected', () => { it.each(getExamples())( 'should detect $exampleName', async ({ exampleName, examplePath }) => { - const fs = new FixtureFilesystem(examplePath); + const fs = new LocalFileSystemDetector(examplePath); const framework = await detectFramework({ fs, frameworkList }); if (!framework) { throw new Error(`Framework not detected for example "${exampleName}".`); diff --git a/packages/fs-detectors/test/unit.get-monorepo-default-settings.test.ts b/packages/fs-detectors/test/unit.get-monorepo-default-settings.test.ts index 9319fab01..eed693c1f 100644 --- a/packages/fs-detectors/test/unit.get-monorepo-default-settings.test.ts +++ b/packages/fs-detectors/test/unit.get-monorepo-default-settings.test.ts @@ -1,13 +1,12 @@ +import os from 'os'; +import path from 'path'; +import { mkdtempSync } from 'fs'; import { getMonorepoDefaultSettings, LocalFileSystemDetector, MissingBuildPipeline, MissingBuildTarget, } from '../src'; -import path from 'path'; -import fs from 'fs'; -import os from 'os'; -import { FixtureFilesystem } from './utils/fixture-filesystem'; describe('getMonorepoDefaultSettings', () => { test('MissingBuildTarget is an error', () => { @@ -69,7 +68,7 @@ describe('getMonorepoDefaultSettings', () => { }, }; - const ffs = new FixtureFilesystem( + const fs = new LocalFileSystemDetector( path.join( __dirname, 'fixtures', @@ -81,16 +80,16 @@ describe('getMonorepoDefaultSettings', () => { packageName, isRoot ? '/' : 'packages/app-1', isRoot ? '/' : '../..', - ffs + fs ); expect(result).toStrictEqual(expectedResultMap[expectedResultKey]); } ); test('returns null when neither nx nor turbo is detected', async () => { - const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'monorepo-test-')); - const lfs = new LocalFileSystemDetector(dir); - const result = await getMonorepoDefaultSettings('', '', '', lfs); + const dir = mkdtempSync(path.join(os.tmpdir(), 'monorepo-test-')); + const fs = new LocalFileSystemDetector(dir); + const result = await getMonorepoDefaultSettings('', '', '', fs); expect(result).toBe(null); }); }); diff --git a/packages/fs-detectors/test/unit.get-project-paths.test.ts b/packages/fs-detectors/test/unit.get-project-paths.test.ts index 421c991de..f80e75721 100644 --- a/packages/fs-detectors/test/unit.get-project-paths.test.ts +++ b/packages/fs-detectors/test/unit.get-project-paths.test.ts @@ -1,7 +1,7 @@ import path from 'path'; import { normalizePath } from '@vercel/build-utils'; import { getProjectPaths, ProjectPath } from '../src/get-project-paths'; -import { FixtureFilesystem } from './utils/fixture-filesystem'; +import { LocalFileSystemDetector } from '../src'; describe.each<{ fixturePath: string; @@ -52,7 +52,7 @@ describe.each<{ it(testName, async () => { const fixture = path.join(__dirname, 'fixtures', fixturePath); - const fs = new FixtureFilesystem(fixture); + const fs = new LocalFileSystemDetector(fixture); const mockReaddir = jest.fn().mockImplementation(fs.readdir); const mockHasPath = jest.fn().mockImplementation(fs.hasPath); fs.readdir = mockReaddir; diff --git a/packages/fs-detectors/test/unit.get-workspaces-package-paths.test.ts b/packages/fs-detectors/test/unit.get-workspaces-package-paths.test.ts index db451c19a..cf686b59c 100644 --- a/packages/fs-detectors/test/unit.get-workspaces-package-paths.test.ts +++ b/packages/fs-detectors/test/unit.get-workspaces-package-paths.test.ts @@ -1,7 +1,7 @@ import path from 'path'; import { getWorkspaces } from '../src/workspaces/get-workspaces'; import { getWorkspacePackagePaths } from '../src/workspaces/get-workspace-package-paths'; -import { FixtureFilesystem } from './utils/fixture-filesystem'; +import { LocalFileSystemDetector } from '../src'; describe.each<[string, string[]]>([ ['21-npm-workspaces', ['/a', '/b']], @@ -32,7 +32,7 @@ describe.each<[string, string[]]>([ it(testName, async () => { const fixture = path.join(__dirname, 'fixtures', fixturePath); - const fs = new FixtureFilesystem(fixture); + const fs = new LocalFileSystemDetector(fixture); const workspaces = await getWorkspaces({ fs }); const actualPackagePaths = ( diff --git a/packages/fs-detectors/test/unit.get-workspaces.test.ts b/packages/fs-detectors/test/unit.get-workspaces.test.ts index 2b77d9d4e..573647c87 100644 --- a/packages/fs-detectors/test/unit.get-workspaces.test.ts +++ b/packages/fs-detectors/test/unit.get-workspaces.test.ts @@ -1,6 +1,6 @@ import path from 'path'; +import { LocalFileSystemDetector } from '../src'; import { getWorkspaces, Workspace } from '../src/workspaces/get-workspaces'; -import { FixtureFilesystem } from './utils/fixture-filesystem'; describe.each<[string, Workspace[]]>([ ['21-npm-workspaces', [{ type: 'npm', rootPath: '/' }]], @@ -34,7 +34,7 @@ describe.each<[string, Workspace[]]>([ it(testName, async () => { const fixture = path.join(__dirname, 'fixtures', fixturePath); - const fs = new FixtureFilesystem(fixture); + const fs = new LocalFileSystemDetector(fixture); const actualWorkspaces = await getWorkspaces({ fs }); diff --git a/packages/fs-detectors/test/unit.local-file-system-detector.test.ts b/packages/fs-detectors/test/unit.local-file-system-detector.test.ts index 4017922e3..6b91c40a0 100644 --- a/packages/fs-detectors/test/unit.local-file-system-detector.test.ts +++ b/packages/fs-detectors/test/unit.local-file-system-detector.test.ts @@ -5,7 +5,7 @@ import { LocalFileSystemDetector, DetectorFilesystem } from '../src'; const tmpdir = path.join(os.tmpdir(), 'local-file-system-test'); -const dirs = ['', 'a', 'a/b']; // root, single-nested, double-nested +const dirs = ['', 'a', `a${path.sep}b`]; // root, single-nested, double-nested const files = ['foo', 'bar']; const filePaths = dirs.flatMap(dir => files.map(file => path.join(dir, file))); @@ -63,12 +63,7 @@ describe('LocalFileSystemDetector', () => { const readdirResults = await Promise.all( dirs.map(dir => localFileSystem.readdir(dir)) ); - const expectedPaths = [ - ...dirs.map(dir => path.join(tmpdir, dir)), - ...filePaths.map(filePath => path.join(tmpdir, filePath)), - ] - .sort() - .slice(1); // drop the first path since its the root + const expectedPaths = [...dirs, ...filePaths].sort().slice(1); // drop the first path since its the root const actualPaths = readdirResults .flatMap(result => result.map(stat => stat.path)) .sort(); diff --git a/packages/fs-detectors/test/utils/fixture-filesystem.ts b/packages/fs-detectors/test/utils/fixture-filesystem.ts deleted file mode 100644 index 4545e3cd1..000000000 --- a/packages/fs-detectors/test/utils/fixture-filesystem.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { promises } from 'fs'; -import path from 'path'; - -import { DetectorFilesystem } from '../../src'; -import { DetectorFilesystemStat } from '../../src/detectors/filesystem'; - -const { stat, readFile, readdir } = promises; - -export class FixtureFilesystem extends DetectorFilesystem { - private rootPath: string; - - constructor(fixturePath: string) { - super(); - - this.rootPath = fixturePath; - } - - async _hasPath(name: string): Promise { - try { - const filePath = path.join(this.rootPath, name); - await stat(filePath); - - return true; - } catch { - return false; - } - } - async _readFile(name: string): Promise { - const filePath = path.join(this.rootPath, name); - return readFile(filePath); - } - async _isFile(name: string): Promise { - const filePath = path.join(this.rootPath, name); - return (await stat(filePath)).isFile(); - } - - async _readdir(name: string): Promise { - const dirPath = path.join(this.rootPath, name); - const files = await readdir(dirPath, { withFileTypes: true }); - - return files.map(file => ({ - name: file.name, - type: file.isFile() ? 'file' : 'dir', - path: path.join(name, file.name), - })); - } - - _chdir(name: string): DetectorFilesystem { - return new FixtureFilesystem(path.join(this.rootPath, name)); - } -}