mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-09 12:57:46 +00:00
[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:
5
.changeset/calm-apes-double.md
Normal file
5
.changeset/calm-apes-double.md
Normal 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.
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"test": "jest --env node --verbose --runInBand --bail test/unit.*test.*",
|
"test": "jest --env node --verbose --runInBand --bail",
|
||||||
"test-unit": "pnpm test"
|
"test-unit": "pnpm test test/unit.*test.*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vercel/error-utils": "1.0.10",
|
"@vercel/error-utils": "1.0.10",
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import { isErrnoException } from '@vercel/error-utils';
|
|||||||
|
|
||||||
export class LocalFileSystemDetector extends DetectorFilesystem {
|
export class LocalFileSystemDetector extends DetectorFilesystem {
|
||||||
private rootPath: string;
|
private rootPath: string;
|
||||||
|
|
||||||
constructor(rootPath: string) {
|
constructor(rootPath: string) {
|
||||||
super();
|
super();
|
||||||
this.rootPath = rootPath;
|
this.rootPath = rootPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _hasPath(name: string): Promise<boolean> {
|
async _hasPath(name: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await fs.stat(this.getFilePath(name));
|
await fs.stat(this.getFilePath(name));
|
||||||
@@ -21,13 +23,16 @@ export class LocalFileSystemDetector extends DetectorFilesystem {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_readFile(name: string): Promise<Buffer> {
|
_readFile(name: string): Promise<Buffer> {
|
||||||
return fs.readFile(this.getFilePath(name));
|
return fs.readFile(this.getFilePath(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
async _isFile(name: string): Promise<boolean> {
|
async _isFile(name: string): Promise<boolean> {
|
||||||
const stat = await fs.stat(this.getFilePath(name));
|
const stat = await fs.stat(this.getFilePath(name));
|
||||||
return stat.isFile();
|
return stat.isFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _readdir(name: string): Promise<DetectorFilesystemStat[]> {
|
async _readdir(name: string): Promise<DetectorFilesystemStat[]> {
|
||||||
const dirPath = this.getFilePath(name);
|
const dirPath = this.getFilePath(name);
|
||||||
const dir = await fs.readdir(dirPath, {
|
const dir = await fs.readdir(dirPath, {
|
||||||
@@ -44,14 +49,22 @@ export class LocalFileSystemDetector extends DetectorFilesystem {
|
|||||||
};
|
};
|
||||||
return dir.map(dirent => ({
|
return dir.map(dirent => ({
|
||||||
name: dirent.name,
|
name: dirent.name,
|
||||||
path: path.join(dirPath, dirent.name),
|
path: path.join(this.getRelativeFilePath(name), dirent.name),
|
||||||
type: getType(dirent),
|
type: getType(dirent),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
_chdir(name: string): DetectorFilesystem {
|
_chdir(name: string): DetectorFilesystem {
|
||||||
return new LocalFileSystemDetector(this.getFilePath(name));
|
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) {
|
private getFilePath(name: string) {
|
||||||
return path.join(this.rootPath, name);
|
return path.join(this.rootPath, this.getRelativeFilePath(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { LocalFileSystemDetector } from '../src';
|
||||||
import { detectFramework } from '../src/detect-framework';
|
import { detectFramework } from '../src/detect-framework';
|
||||||
import monorepoManagers from '../src/monorepos/monorepo-managers';
|
import monorepoManagers from '../src/monorepos/monorepo-managers';
|
||||||
import { FixtureFilesystem } from './utils/fixture-filesystem';
|
|
||||||
|
|
||||||
describe('monorepo-managers', () => {
|
describe('monorepo-managers', () => {
|
||||||
describe.each([
|
describe.each([
|
||||||
@@ -17,7 +17,7 @@ describe('monorepo-managers', () => {
|
|||||||
|
|
||||||
it(testName, async () => {
|
it(testName, async () => {
|
||||||
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
||||||
const fs = new FixtureFilesystem(fixture);
|
const fs = new LocalFileSystemDetector(fixture);
|
||||||
|
|
||||||
const result = await detectFramework({
|
const result = await detectFramework({
|
||||||
fs,
|
fs,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { packageManagers, detectFramework } from '../src';
|
import {
|
||||||
import { FixtureFilesystem } from './utils/fixture-filesystem';
|
packageManagers,
|
||||||
|
detectFramework,
|
||||||
|
LocalFileSystemDetector,
|
||||||
|
} from '../src';
|
||||||
|
|
||||||
describe('package-managers', () => {
|
describe('package-managers', () => {
|
||||||
describe.each([
|
describe.each([
|
||||||
@@ -16,7 +19,7 @@ describe('package-managers', () => {
|
|||||||
|
|
||||||
it(testName, async () => {
|
it(testName, async () => {
|
||||||
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
||||||
const fs = new FixtureFilesystem(fixture);
|
const fs = new LocalFileSystemDetector(fixture);
|
||||||
|
|
||||||
const result = await detectFramework({
|
const result = await detectFramework({
|
||||||
fs,
|
fs,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { LocalFileSystemDetector } from '../src';
|
||||||
import { detectFramework } from '../src/detect-framework';
|
import { detectFramework } from '../src/detect-framework';
|
||||||
import workspaceManagers from '../src/workspaces/workspace-managers';
|
import workspaceManagers from '../src/workspaces/workspace-managers';
|
||||||
import { FixtureFilesystem } from './utils/fixture-filesystem';
|
|
||||||
|
|
||||||
describe('workspace-managers', () => {
|
describe('workspace-managers', () => {
|
||||||
describe.each([
|
describe.each([
|
||||||
@@ -19,7 +19,7 @@ describe('workspace-managers', () => {
|
|||||||
|
|
||||||
it(testName, async () => {
|
it(testName, async () => {
|
||||||
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
||||||
const fs = new FixtureFilesystem(fixture);
|
const fs = new LocalFileSystemDetector(fixture);
|
||||||
|
|
||||||
const result = await detectFramework({
|
const result = await detectFramework({
|
||||||
fs,
|
fs,
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import frameworkList from '@vercel/frameworks';
|
import frameworkList from '@vercel/frameworks';
|
||||||
import { detectFramework } from '../src';
|
import { detectFramework, LocalFileSystemDetector } from '../src';
|
||||||
import { FixtureFilesystem } from './utils/fixture-filesystem';
|
|
||||||
import { getExamples } from '../../../examples/__tests__/test-utils';
|
import { getExamples } from '../../../examples/__tests__/test-utils';
|
||||||
|
|
||||||
describe('examples should be detected', () => {
|
describe('examples should be detected', () => {
|
||||||
it.each(getExamples())(
|
it.each(getExamples())(
|
||||||
'should detect $exampleName',
|
'should detect $exampleName',
|
||||||
async ({ exampleName, examplePath }) => {
|
async ({ exampleName, examplePath }) => {
|
||||||
const fs = new FixtureFilesystem(examplePath);
|
const fs = new LocalFileSystemDetector(examplePath);
|
||||||
const framework = await detectFramework({ fs, frameworkList });
|
const framework = await detectFramework({ fs, frameworkList });
|
||||||
if (!framework) {
|
if (!framework) {
|
||||||
throw new Error(`Framework not detected for example "${exampleName}".`);
|
throw new Error(`Framework not detected for example "${exampleName}".`);
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
|
import os from 'os';
|
||||||
|
import path from 'path';
|
||||||
|
import { mkdtempSync } from 'fs';
|
||||||
import {
|
import {
|
||||||
getMonorepoDefaultSettings,
|
getMonorepoDefaultSettings,
|
||||||
LocalFileSystemDetector,
|
LocalFileSystemDetector,
|
||||||
MissingBuildPipeline,
|
MissingBuildPipeline,
|
||||||
MissingBuildTarget,
|
MissingBuildTarget,
|
||||||
} from '../src';
|
} from '../src';
|
||||||
import path from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import os from 'os';
|
|
||||||
import { FixtureFilesystem } from './utils/fixture-filesystem';
|
|
||||||
|
|
||||||
describe('getMonorepoDefaultSettings', () => {
|
describe('getMonorepoDefaultSettings', () => {
|
||||||
test('MissingBuildTarget is an error', () => {
|
test('MissingBuildTarget is an error', () => {
|
||||||
@@ -69,7 +68,7 @@ describe('getMonorepoDefaultSettings', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ffs = new FixtureFilesystem(
|
const fs = new LocalFileSystemDetector(
|
||||||
path.join(
|
path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
'fixtures',
|
'fixtures',
|
||||||
@@ -81,16 +80,16 @@ describe('getMonorepoDefaultSettings', () => {
|
|||||||
packageName,
|
packageName,
|
||||||
isRoot ? '/' : 'packages/app-1',
|
isRoot ? '/' : 'packages/app-1',
|
||||||
isRoot ? '/' : '../..',
|
isRoot ? '/' : '../..',
|
||||||
ffs
|
fs
|
||||||
);
|
);
|
||||||
expect(result).toStrictEqual(expectedResultMap[expectedResultKey]);
|
expect(result).toStrictEqual(expectedResultMap[expectedResultKey]);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
test('returns null when neither nx nor turbo is detected', async () => {
|
test('returns null when neither nx nor turbo is detected', async () => {
|
||||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'monorepo-test-'));
|
const dir = mkdtempSync(path.join(os.tmpdir(), 'monorepo-test-'));
|
||||||
const lfs = new LocalFileSystemDetector(dir);
|
const fs = new LocalFileSystemDetector(dir);
|
||||||
const result = await getMonorepoDefaultSettings('', '', '', lfs);
|
const result = await getMonorepoDefaultSettings('', '', '', fs);
|
||||||
expect(result).toBe(null);
|
expect(result).toBe(null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { normalizePath } from '@vercel/build-utils';
|
import { normalizePath } from '@vercel/build-utils';
|
||||||
import { getProjectPaths, ProjectPath } from '../src/get-project-paths';
|
import { getProjectPaths, ProjectPath } from '../src/get-project-paths';
|
||||||
import { FixtureFilesystem } from './utils/fixture-filesystem';
|
import { LocalFileSystemDetector } from '../src';
|
||||||
|
|
||||||
describe.each<{
|
describe.each<{
|
||||||
fixturePath: string;
|
fixturePath: string;
|
||||||
@@ -52,7 +52,7 @@ describe.each<{
|
|||||||
|
|
||||||
it(testName, async () => {
|
it(testName, async () => {
|
||||||
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
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 mockReaddir = jest.fn().mockImplementation(fs.readdir);
|
||||||
const mockHasPath = jest.fn().mockImplementation(fs.hasPath);
|
const mockHasPath = jest.fn().mockImplementation(fs.hasPath);
|
||||||
fs.readdir = mockReaddir;
|
fs.readdir = mockReaddir;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { getWorkspaces } from '../src/workspaces/get-workspaces';
|
import { getWorkspaces } from '../src/workspaces/get-workspaces';
|
||||||
import { getWorkspacePackagePaths } from '../src/workspaces/get-workspace-package-paths';
|
import { getWorkspacePackagePaths } from '../src/workspaces/get-workspace-package-paths';
|
||||||
import { FixtureFilesystem } from './utils/fixture-filesystem';
|
import { LocalFileSystemDetector } from '../src';
|
||||||
|
|
||||||
describe.each<[string, string[]]>([
|
describe.each<[string, string[]]>([
|
||||||
['21-npm-workspaces', ['/a', '/b']],
|
['21-npm-workspaces', ['/a', '/b']],
|
||||||
@@ -32,7 +32,7 @@ describe.each<[string, string[]]>([
|
|||||||
|
|
||||||
it(testName, async () => {
|
it(testName, async () => {
|
||||||
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
||||||
const fs = new FixtureFilesystem(fixture);
|
const fs = new LocalFileSystemDetector(fixture);
|
||||||
|
|
||||||
const workspaces = await getWorkspaces({ fs });
|
const workspaces = await getWorkspaces({ fs });
|
||||||
const actualPackagePaths = (
|
const actualPackagePaths = (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { LocalFileSystemDetector } from '../src';
|
||||||
import { getWorkspaces, Workspace } from '../src/workspaces/get-workspaces';
|
import { getWorkspaces, Workspace } from '../src/workspaces/get-workspaces';
|
||||||
import { FixtureFilesystem } from './utils/fixture-filesystem';
|
|
||||||
|
|
||||||
describe.each<[string, Workspace[]]>([
|
describe.each<[string, Workspace[]]>([
|
||||||
['21-npm-workspaces', [{ type: 'npm', rootPath: '/' }]],
|
['21-npm-workspaces', [{ type: 'npm', rootPath: '/' }]],
|
||||||
@@ -34,7 +34,7 @@ describe.each<[string, Workspace[]]>([
|
|||||||
|
|
||||||
it(testName, async () => {
|
it(testName, async () => {
|
||||||
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
const fixture = path.join(__dirname, 'fixtures', fixturePath);
|
||||||
const fs = new FixtureFilesystem(fixture);
|
const fs = new LocalFileSystemDetector(fixture);
|
||||||
|
|
||||||
const actualWorkspaces = await getWorkspaces({ fs });
|
const actualWorkspaces = await getWorkspaces({ fs });
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { LocalFileSystemDetector, DetectorFilesystem } from '../src';
|
|||||||
|
|
||||||
const tmpdir = path.join(os.tmpdir(), 'local-file-system-test');
|
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 files = ['foo', 'bar'];
|
||||||
const filePaths = dirs.flatMap(dir => files.map(file => path.join(dir, file)));
|
const filePaths = dirs.flatMap(dir => files.map(file => path.join(dir, file)));
|
||||||
|
|
||||||
@@ -63,12 +63,7 @@ describe('LocalFileSystemDetector', () => {
|
|||||||
const readdirResults = await Promise.all(
|
const readdirResults = await Promise.all(
|
||||||
dirs.map(dir => localFileSystem.readdir(dir))
|
dirs.map(dir => localFileSystem.readdir(dir))
|
||||||
);
|
);
|
||||||
const expectedPaths = [
|
const expectedPaths = [...dirs, ...filePaths].sort().slice(1); // drop the first path since its the root
|
||||||
...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 actualPaths = readdirResults
|
const actualPaths = readdirResults
|
||||||
.flatMap(result => result.map(stat => stat.path))
|
.flatMap(result => result.map(stat => stat.path))
|
||||||
.sort();
|
.sort();
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user