Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vscode): implemented nearestChildDartFrogProjects #1015

Merged
merged 12 commits into from
Sep 11, 2023
Merged
184 changes: 184 additions & 0 deletions extensions/vscode/src/test/suite/utils/dart-frog-structure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,190 @@ suite("nearestParentDartFrogProject", () => {
});
});

suite("nearestChildDartFrogProjects", () => {
let fsStub: any;
let nearestChildDartFrogProjects: any;

beforeEach(() => {
fsStub = {
existsSync: sinon.stub(),
readFileSync: sinon.stub(),
statSync: sinon.stub(),
readdirSync: sinon.stub(),
};

nearestChildDartFrogProjects = proxyquire(
"../../../utils/dart-frog-structure",
{
fs: fsStub,
}
).nearestChildDartFrogProjects;
});

afterEach(() => {
sinon.restore();
});

test("returns a single path when the file path is a Dart Frog project", () => {
const filePath = "/home/project";

const dartFrogPubspecRoutesPath = path.join(filePath, "routes");
const dartFrogPubspecPath = path.join(filePath, "pubspec.yaml");
fsStub.existsSync.withArgs(filePath).returns(true);
fsStub.statSync.withArgs(filePath).returns({
isDirectory: () => true,
});
fsStub.readdirSync.withArgs(filePath).returns([]);
fsStub.existsSync.withArgs(dartFrogPubspecRoutesPath).returns(true);
fsStub.existsSync.withArgs(dartFrogPubspecPath).returns(true);
fsStub.readFileSync
.withArgs(dartFrogPubspecPath, "utf-8")
.returns(validPubspecYaml);

const dartFrogProjects = nearestChildDartFrogProjects(filePath);

assert.deepEqual(dartFrogProjects, [filePath]);
});

test("returns the path to all the child Dart Frog projects", () => {
fsStub.existsSync.returns(false);

const filePath = "/home/project";
fsStub.existsSync.withArgs(filePath).returns(true);
fsStub.statSync.withArgs(filePath).returns({
isDirectory: () => true,
});
fsStub.readdirSync
.withArgs(filePath)
.returns(["file.dart", "frog1", "frog2", "subdirectory", "flutter"]);

const fileDartPath = "/home/project/file.dart";
fsStub.existsSync.withArgs(fileDartPath).returns(true);
fsStub.statSync.withArgs(fileDartPath).returns({
isDirectory: () => false,
});

const dartFrogPath1 = "/home/project/frog1";
const dartFrogPubspecRoutesPath1 = path.join(dartFrogPath1, "routes");
const dartFrogPubspecPath1 = path.join(dartFrogPath1, "pubspec.yaml");
fsStub.existsSync.withArgs(dartFrogPath1).returns(true);
fsStub.statSync.withArgs(dartFrogPath1).returns({
isDirectory: () => true,
});
fsStub.readdirSync.withArgs(dartFrogPath1).returns([]);
fsStub.existsSync.withArgs(dartFrogPubspecRoutesPath1).returns(true);
fsStub.existsSync.withArgs(dartFrogPubspecPath1).returns(true);
fsStub.readFileSync
.withArgs(dartFrogPubspecPath1, "utf-8")
.returns(validPubspecYaml);

const dartFrogPath2 = "/home/project/frog2";
const dartFrogPubspecRoutesPath2 = path.join(dartFrogPath2, "routes");
const dartFrogPubspecPath2 = path.join(dartFrogPath2, "pubspec.yaml");
fsStub.existsSync.withArgs(dartFrogPath2).returns(true);
fsStub.statSync.withArgs(dartFrogPath2).returns({
isDirectory: () => true,
});
fsStub.readdirSync.withArgs(dartFrogPath2).returns([]);
fsStub.existsSync.withArgs(dartFrogPubspecRoutesPath2).returns(true);
fsStub.existsSync.withArgs(dartFrogPubspecPath2).returns(true);
fsStub.readFileSync
.withArgs(dartFrogPubspecPath2, "utf-8")
.returns(validPubspecYaml);

const subdirectory = "/home/project/subdirectory";
fsStub.existsSync.withArgs(subdirectory).returns(true);
fsStub.statSync.withArgs(subdirectory).returns({
isDirectory: () => true,
});
fsStub.readdirSync.withArgs(subdirectory).returns(["frog3"]);

const dartFrogPath3 = "/home/project/subdirectory/frog3";
const dartFrogPubspecRoutesPath3 = path.join(dartFrogPath3, "routes");
const dartFrogPubspecPath3 = path.join(dartFrogPath3, "pubspec.yaml");
fsStub.existsSync.withArgs(dartFrogPath3).returns(true);
fsStub.statSync.withArgs(dartFrogPath3).returns({
isDirectory: () => true,
});
fsStub.readdirSync.withArgs(dartFrogPath3).returns([]);
fsStub.existsSync.withArgs(dartFrogPubspecRoutesPath3).returns(true);
fsStub.existsSync.withArgs(dartFrogPubspecPath3).returns(true);
fsStub.readFileSync
.withArgs(dartFrogPubspecPath3, "utf-8")
.returns(validPubspecYaml);

const flutterPath = "/home/project/flutter";
const flutterPubspecPath = path.join(flutterPath, "pubspec.yaml");
fsStub.existsSync.withArgs(flutterPath).returns(true);
fsStub.statSync.withArgs(flutterPath).returns({
isDirectory: () => true,
});
fsStub.readdirSync.withArgs(flutterPath).returns([]);
fsStub.existsSync.withArgs(flutterPubspecPath).returns(true);
fsStub.readFileSync
.withArgs(flutterPubspecPath, "utf-8")
.returns(invalidPubspecYaml);

const dartFrogProjects = nearestChildDartFrogProjects(filePath);

assert.deepEqual(dartFrogProjects, [
dartFrogPath1,
dartFrogPath2,
dartFrogPath3,
]);
});

suite("returns undefined", () => {
test("when path does not exist", () => {
const filePath = "/home/project/routes/animals/frog.dart";
fsStub.existsSync.withArgs(filePath).returns(false);

const result = nearestChildDartFrogProjects(filePath);

assert.equal(result, undefined);
});

test("when path is not a directory", () => {
const filePath = "/home/project/routes/animals/frog.dart";
fsStub.existsSync.withArgs(filePath).returns(true);
fsStub.statSync.withArgs(filePath).returns({
isDirectory: () => false,
});

const result = nearestChildDartFrogProjects(filePath);

assert.equal(result, undefined);
});

test("when subdirectory is not a Dart Frog project", () => {
const filePath = "/home/project";
fsStub.existsSync.withArgs(filePath).returns(true);
fsStub.statSync.withArgs(filePath).returns({
isDirectory: () => true,
});
fsStub.readdirSync.withArgs(filePath).returns(["frog"]);

const dartFrogPath = "/home/project/frog";
const dartFrogPubspecRoutesPath = path.join(dartFrogPath, "routes");
const dartFrogPubspecPath = path.join(dartFrogPath, "pubspec.yaml");
fsStub.existsSync.withArgs(dartFrogPath).returns(true);
fsStub.statSync.withArgs(dartFrogPath).returns({
isDirectory: () => true,
});
fsStub.readdirSync.withArgs(dartFrogPath).returns([]);
fsStub.existsSync.withArgs(dartFrogPubspecRoutesPath).returns(true);
fsStub.existsSync.withArgs(dartFrogPubspecPath).returns(true);
fsStub.readFileSync
.withArgs(dartFrogPubspecPath, "utf-8")
.returns(invalidPubspecYaml);

const result = nearestChildDartFrogProjects(filePath);

assert.equal(result, undefined);
});
});
});

suite("isDartFrogProject", () => {
let fsStub: any;
let isDartFrogProject: any;
Expand Down
60 changes: 60 additions & 0 deletions extensions/vscode/src/utils/dart-frog-structure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,66 @@ export function nearestParentDartFrogProject(
return undefined;
}

/**
* Finds all Dart Frog projects that are children of a directory.
*
* Nested Dart Frog projects are not considered. Therefore, if the
* {@link filePath} is a Dart Frog project, then only that project is reported.
* The same logic applies to subdirectories. If a subdirectory is also a Dart
* Frog project, then all of its subdirectories are ignored and only the
* shallowest Dart Frog project is reported.
*
* @param filePath The path to the directory to check for.
* @returns {Array<string> | undefined} A set of paths to Dart Frog projects
* that are children of the {@link filePath}, or `undefined` if there are no
* Dart Frog projects in the {@link filePath}.
*/
export function nearestChildDartFrogProjects(
filePath: string
): Array<string> | undefined {
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isDirectory()) {
return undefined;
}

if (isDartFrogProject(filePath)) {
return [filePath];
}

const dartFrogProjects = new Set<string>();

let currentSubdirectories = fs
.readdirSync(filePath)
.map((file: string) => path.join(filePath, file))
.filter((file: string) => fs.statSync(file).isDirectory());

while (currentSubdirectories.length > 0) {
for (let i = 0; i < currentSubdirectories.length; i++) {
const subdirectory = currentSubdirectories[i];
if (isDartFrogProject(subdirectory)) {
dartFrogProjects.add(subdirectory);
currentSubdirectories.splice(i, 1);
i--;
}
}

const nextSubdirectories: string[] = [];
for (const subdirectory of currentSubdirectories) {
const subdirectorySubdirectories = fs
.readdirSync(subdirectory)
.map((file: string) => path.join(subdirectory, file))
.filter((file: string) => fs.statSync(file).isDirectory());
nextSubdirectories.push(...subdirectorySubdirectories);
}
currentSubdirectories = nextSubdirectories;
}

if (dartFrogProjects.size === 0) {
return undefined;
}

return Array.from(dartFrogProjects);
}

/**
* Determines if a {@link filePath} is a Dart Frog project.
*
Expand Down