1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

feat: Add support for linking explicit files in addition to directories

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>
Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
Iku-turso 2023-04-21 14:44:08 +03:00
parent d61deb8b73
commit fb6bf2143c
3 changed files with 124 additions and 45 deletions

View File

@ -0,0 +1,14 @@
import { getInjectable } from "@ogre-tools/injectable";
import fse from "fs-extra";
export type IsFileOrDirectory = (path: string) => Promise<"file" | "dir">;
export const isFileOrDirectoryInjectable = getInjectable({
id: "is-file-or-directory",
instantiate: (): IsFileOrDirectory => async (path: string) => {
const stat = await fse.stat(path);
return stat.isDirectory() ? "dir" : "file";
},
});

View File

@ -7,6 +7,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import { pipeline } from "@ogre-tools/fp"; import { pipeline } from "@ogre-tools/fp";
import { getLensLinkDirectoryInjectable } from "./get-lens-link-directory.injectable"; import { getLensLinkDirectoryInjectable } from "./get-lens-link-directory.injectable";
import path from "path"; import path from "path";
import { isFileOrDirectoryInjectable } from "./fs/is-file-or-directory.injectable";
const shouldBeGlobbed = (possibleGlobString: string) => possibleGlobString.includes("*"); const shouldBeGlobbed = (possibleGlobString: string) => possibleGlobString.includes("*");
const simplifyGlobbing = new RegExp("(\\/\\*\\/\\*\\*|\\/\\*\\*|\\/\\*\\*\\/\\*|\\/\\*)$"); const simplifyGlobbing = new RegExp("(\\/\\*\\/\\*\\*|\\/\\*\\*|\\/\\*\\*\\/\\*|\\/\\*)$");
@ -19,6 +20,7 @@ export const getSymlinkPathsInjectable = getInjectable({
const glob = di.inject(globInjectable); const glob = di.inject(globInjectable);
const resolvePath = di.inject(resolvePathInjectable); const resolvePath = di.inject(resolvePathInjectable);
const getLensLinkDirectory = di.inject(getLensLinkDirectoryInjectable); const getLensLinkDirectory = di.inject(getLensLinkDirectoryInjectable);
const isFileOrDirectory = di.inject(isFileOrDirectoryInjectable);
return async (packageJsons: PackageJsonAndPath[]) => { return async (packageJsons: PackageJsonAndPath[]) => {
return pipeline( return pipeline(
@ -39,6 +41,22 @@ export const getSymlinkPathsInjectable = getInjectable({
globbeds = await glob(toBeGlobbed, { cwd: moduleDirectory }); globbeds = await glob(toBeGlobbed, { cwd: moduleDirectory });
} }
const notGlobbedFilesOrDirectories = await pipeline(
toNotBeGlobbed,
map(async (fileOrDirectory) => {
const target = resolvePath(moduleDirectory, fileOrDirectory);
return {
target,
source: resolvePath(lensLinkDirectory, fileOrDirectory),
type: await isFileOrDirectory(target),
};
}),
awaitAll,
);
return [ return [
{ {
target: packageJsonPath, target: packageJsonPath,
@ -52,11 +70,7 @@ export const getSymlinkPathsInjectable = getInjectable({
type: "file" as const, type: "file" as const,
})), })),
...toNotBeGlobbed.map((fileOrDirectory) => ({ ...notGlobbedFilesOrDirectories,
target: resolvePath(moduleDirectory, fileOrDirectory),
source: resolvePath(lensLinkDirectory, fileOrDirectory),
type: "dir" as const,
})),
]; ];
}), }),

View File

@ -21,6 +21,8 @@ import { removeDirectoryInjectable } from "./fs/remove-directory.injectable";
import { getDi } from "./get-di"; import { getDi } from "./get-di";
import type { Glob } from "./fs/glob.injectable"; import type { Glob } from "./fs/glob.injectable";
import { globInjectable } from "./fs/glob.injectable"; import { globInjectable } from "./fs/glob.injectable";
import type { IsFileOrDirectory } from "./fs/is-file-or-directory.injectable";
import { isFileOrDirectoryInjectable } from "./fs/is-file-or-directory.injectable";
describe("lens-link", () => { describe("lens-link", () => {
let lensLink: LensLink; let lensLink: LensLink;
@ -31,6 +33,7 @@ describe("lens-link", () => {
let ensureDirectoryMock: AsyncFnMock<EnsureDirectory>; let ensureDirectoryMock: AsyncFnMock<EnsureDirectory>;
let removeDirectoryMock: AsyncFnMock<RemoveDirectory>; let removeDirectoryMock: AsyncFnMock<RemoveDirectory>;
let globMock: AsyncFnMock<Glob>; let globMock: AsyncFnMock<Glob>;
let isFileOrDirectoryMock: AsyncFnMock<IsFileOrDirectory>;
beforeEach(() => { beforeEach(() => {
existsMock = asyncFn(); existsMock = asyncFn();
@ -39,6 +42,7 @@ describe("lens-link", () => {
createSymlinkMock = asyncFn(); createSymlinkMock = asyncFn();
ensureDirectoryMock = asyncFn(); ensureDirectoryMock = asyncFn();
removeDirectoryMock = asyncFn(); removeDirectoryMock = asyncFn();
isFileOrDirectoryMock = asyncFn();
globMock = asyncFn(); globMock = asyncFn();
const di = getDi(); const di = getDi();
@ -52,6 +56,7 @@ describe("lens-link", () => {
di.override(ensureDirectoryInjectable, () => ensureDirectoryMock); di.override(ensureDirectoryInjectable, () => ensureDirectoryMock);
di.override(removeDirectoryInjectable, () => removeDirectoryMock); di.override(removeDirectoryInjectable, () => removeDirectoryMock);
di.override(globInjectable, () => globMock); di.override(globInjectable, () => globMock);
di.override(isFileOrDirectoryInjectable, () => isFileOrDirectoryMock);
lensLink = di.inject(lensLinkInjectable); lensLink = di.inject(lensLinkInjectable);
}); });
@ -235,10 +240,13 @@ describe("lens-link", () => {
]); ]);
}); });
describe("when creation of Lens link directories resolve", () => { describe("when creation of Lens link directories and files or directories are identified is resolve", () => {
beforeEach(async () => { beforeEach(async () => {
await ensureDirectoryMock.resolve(); await ensureDirectoryMock.resolve();
await ensureDirectoryMock.resolve(); await ensureDirectoryMock.resolve();
await isFileOrDirectoryMock.resolve("dir");
await isFileOrDirectoryMock.resolve("dir");
}); });
it("creates the symlinks to scoped directories", () => { it("creates the symlinks to scoped directories", () => {
@ -327,9 +335,13 @@ describe("lens-link", () => {
expect(createSymlinkMock).not.toHaveBeenCalled(); expect(createSymlinkMock).not.toHaveBeenCalled();
}); });
describe("when globbing resolves", () => { describe("when globbing resolves and files or directories are identified", () => {
beforeEach(async () => { beforeEach(async () => {
await globMock.resolve(["/some-directory/some-module/some-file-from-glob.txt"]); await globMock.resolve(["/some-directory/some-module/some-file-from-glob.txt"]);
await isFileOrDirectoryMock.resolve("dir");
await isFileOrDirectoryMock.resolve("dir");
await isFileOrDirectoryMock.resolve("dir");
}); });
it("creates the symlinks to files and directories that were both globbed and that avoided globbing", () => { it("creates the symlinks to files and directories that were both globbed and that avoided globbing", () => {
@ -381,7 +393,7 @@ describe("lens-link", () => {
await readJsonFileMock.resolveSpecific(([path]) => path === "/some-directory/some-module/package.json", { await readJsonFileMock.resolveSpecific(([path]) => path === "/some-directory/some-module/package.json", {
name: "some-module", name: "some-module",
files: ["some-build-directory"], files: ["some-build-directory", "some-file"],
main: "some-build-directory/index.js", main: "some-build-directory/index.js",
}); });
@ -439,7 +451,7 @@ describe("lens-link", () => {
}); });
}); });
describe("given Lens link directories does not exist", () => { describe("given Lens link directories do not exist", () => {
beforeEach(async () => { beforeEach(async () => {
await existsMock.resolve(false); await existsMock.resolve(false);
await existsMock.resolve(false); await existsMock.resolve(false);
@ -462,50 +474,89 @@ describe("lens-link", () => {
await ensureDirectoryMock.resolve(); await ensureDirectoryMock.resolve();
}); });
it("creates the symlinks", () => { it("doesn't create symlinks yet", () => {
expect(createSymlinkMock.mock.calls).toEqual([ expect(createSymlinkMock).not.toHaveBeenCalled();
[ });
"/some-directory/some-module/package.json",
"/some-directory/some-project/node_modules/some-module/package.json", it("calls for if symlinks to be created are for files or directories", () => {
"file", expect(isFileOrDirectoryMock.mock.calls).toEqual([
], ["/some-directory/some-module/some-build-directory"],
[ ["/some-directory/some-module/some-file"],
"/some-directory/some-module/some-build-directory", ["/some-other-directory/some-other-module/some-other-build-directory"],
"/some-directory/some-project/node_modules/some-module/some-build-directory",
"dir",
],
[
"/some-other-directory/some-other-module/package.json",
"/some-directory/some-project/node_modules/some-other-module/package.json",
"file",
],
[
"/some-other-directory/some-other-module/some-other-build-directory",
"/some-directory/some-project/node_modules/some-other-module/some-other-build-directory",
"dir",
],
]); ]);
}); });
it("given all symlink creations have not resolved, does not resolve yet", async () => { describe("given calls for files or directories resolve", () => {
createSymlinkMock.resolve(); beforeEach(async () => {
createSymlinkMock.resolve(); await isFileOrDirectoryMock.resolveSpecific(
createSymlinkMock.resolve(); ["/some-directory/some-module/some-build-directory"],
"dir",
);
const promiseStatus = await getPromiseStatus(actualPromise); await isFileOrDirectoryMock.resolveSpecific(["/some-directory/some-module/some-file"], "file");
expect(promiseStatus.fulfilled).toBe(false); await isFileOrDirectoryMock.resolveSpecific(
}); ["/some-other-directory/some-other-module/some-other-build-directory"],
"dir",
);
});
it("when symlink creations resolve, ends script", async () => { it("creates the symlinks", () => {
createSymlinkMock.resolve(); expect(createSymlinkMock.mock.calls).toEqual([
createSymlinkMock.resolve(); [
createSymlinkMock.resolve(); "/some-directory/some-module/package.json",
createSymlinkMock.resolve(); "/some-directory/some-project/node_modules/some-module/package.json",
"file",
],
const promiseStatus = await getPromiseStatus(actualPromise); [
"/some-directory/some-module/some-build-directory",
"/some-directory/some-project/node_modules/some-module/some-build-directory",
"dir",
],
expect(promiseStatus.fulfilled).toBe(true); [
"/some-directory/some-module/some-file",
"/some-directory/some-project/node_modules/some-module/some-file",
"file",
],
[
"/some-other-directory/some-other-module/package.json",
"/some-directory/some-project/node_modules/some-other-module/package.json",
"file",
],
[
"/some-other-directory/some-other-module/some-other-build-directory",
"/some-directory/some-project/node_modules/some-other-module/some-other-build-directory",
"dir",
],
]);
});
it("given all symlink creations have not resolved, does not resolve yet", async () => {
createSymlinkMock.resolve();
createSymlinkMock.resolve();
createSymlinkMock.resolve();
createSymlinkMock.resolve();
const promiseStatus = await getPromiseStatus(actualPromise);
expect(promiseStatus.fulfilled).toBe(false);
});
it("when symlink creations resolve, ends script", async () => {
createSymlinkMock.resolve();
createSymlinkMock.resolve();
createSymlinkMock.resolve();
createSymlinkMock.resolve();
createSymlinkMock.resolve();
const promiseStatus = await getPromiseStatus(actualPromise);
expect(promiseStatus.fulfilled).toBe(true);
});
}); });
}); });
}); });