[fs-detectors] Use LocalFileSystemDetector instead of FixtureFilesystem (#10100)

The code for these two are almost identical, so consolidate into one codebase.

Also adjusts the `pnpm test` script to allow for specifying a file name to be executed, instead of running all tests.
This commit is contained in:
Nathan Rajlich
2023-06-13 16:30:00 -07:00
committed by GitHub
parent d61a1a7988
commit 42c0b32a8d
13 changed files with 50 additions and 87 deletions

View File

@@ -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.

View File

@@ -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",

View File

@@ -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<boolean> {
try {
await fs.stat(this.getFilePath(name));
@@ -21,13 +23,16 @@ export class LocalFileSystemDetector extends DetectorFilesystem {
throw err;
}
}
_readFile(name: string): Promise<Buffer> {
return fs.readFile(this.getFilePath(name));
}
async _isFile(name: string): Promise<boolean> {
const stat = await fs.stat(this.getFilePath(name));
return stat.isFile();
}
async _readdir(name: string): Promise<DetectorFilesystemStat[]> {
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));
}
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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}".`);

View File

@@ -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);
});
});

View File

@@ -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;

View File

@@ -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 = (

View File

@@ -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 });

View File

@@ -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();

View File

@@ -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<boolean> {
try {
const filePath = path.join(this.rootPath, name);
await stat(filePath);
return true;
} catch {
return false;
}
}
async _readFile(name: string): Promise<Buffer> {
const filePath = path.join(this.rootPath, name);
return readFile(filePath);
}
async _isFile(name: string): Promise<boolean> {
const filePath = path.join(this.rootPath, name);
return (await stat(filePath)).isFile();
}
async _readdir(name: string): Promise<DetectorFilesystemStat[]> {
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));
}
}