mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
chore: Migrate to using a DI-container to ease complex dependencies
Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
parent
3c9abdb015
commit
21e46cd4b2
@ -1,13 +1,8 @@
|
|||||||
import { lensLinkFor } from "./src/lens-link";
|
import { getDi } from "./src/get-di";
|
||||||
import path from "path";
|
import lensLinkInjectable from "./src/lens-link.injectable";
|
||||||
|
|
||||||
const lensIde = path.join("/Users/jsavolainen/Documents/work/test-lens-link", "lens-ide");
|
const di = getDi();
|
||||||
|
|
||||||
const mikkoFeature = path.join("/Users/jsavolainen/Documents/work/test-lens-link", "mikko-feature");
|
const lensLink = di.inject(lensLinkInjectable);
|
||||||
|
|
||||||
const lensLink = lensLinkFor();
|
lensLink();
|
||||||
|
|
||||||
lensLink({
|
|
||||||
targetDirectory: mikkoFeature,
|
|
||||||
toDirectory: lensIde,
|
|
||||||
});
|
|
||||||
|
|||||||
@ -19,7 +19,11 @@
|
|||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://github.com/lensapp/lens",
|
"homepage": "https://github.com/lensapp/lens",
|
||||||
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ogre-tools/fp": "^15.3.1",
|
||||||
|
"@ogre-tools/injectable": "^15.3.1",
|
||||||
|
"@ogre-tools/injectable-extension-for-auto-registration": "^15.3.1",
|
||||||
"fs-extra": "^9.0.1"
|
"fs-extra": "^9.0.1"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
import { awaitAll } from "./await-all";
|
|
||||||
import type { GetLensLinkDirectory } from "./get-lens-link-directory";
|
|
||||||
import type { EnsureDirectory } from "./lens-link";
|
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
|
||||||
import { map } from "lodash/fp";
|
|
||||||
import type { PackageJsonAndPath } from "./package-json-and-path";
|
|
||||||
|
|
||||||
export type CreateLensLinkDirectories = (packageJsons: PackageJsonAndPath[]) => Promise<void>;
|
|
||||||
|
|
||||||
export const createLensLinkDirectoriesFor =
|
|
||||||
(getLensLinkDirectory: GetLensLinkDirectory, ensureDirectory: EnsureDirectory) =>
|
|
||||||
async (packageJsons: PackageJsonAndPath[]) => {
|
|
||||||
await pipeline(
|
|
||||||
packageJsons,
|
|
||||||
map(({ content: { name } }) => getLensLinkDirectory(name)),
|
|
||||||
map(ensureDirectory),
|
|
||||||
awaitAll,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import { awaitAll } from "./await-all";
|
||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
import { map } from "lodash/fp";
|
||||||
|
import type { PackageJsonAndPath } from "./package-json-and-path";
|
||||||
|
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { ensureDirectoryInjectable } from "./fs/ensure-directory.injectable";
|
||||||
|
import { getLensLinkDirectoryInjectable } from "./get-lens-link-directory.injectable";
|
||||||
|
|
||||||
|
export type CreateLensLinkDirectories = (packageJsons: PackageJsonAndPath[]) => Promise<void>;
|
||||||
|
|
||||||
|
export const createLensLinkDirectoriesInjectable = getInjectable({
|
||||||
|
id: "create-lens-link-directories",
|
||||||
|
|
||||||
|
instantiate: (di): CreateLensLinkDirectories => {
|
||||||
|
const getLensLinkDirectory = di.inject(getLensLinkDirectoryInjectable);
|
||||||
|
const ensureDirectory = di.inject(ensureDirectoryInjectable);
|
||||||
|
|
||||||
|
return async (packageJsons: PackageJsonAndPath[]) => {
|
||||||
|
await pipeline(
|
||||||
|
packageJsons,
|
||||||
|
map(({ content: { name } }) => getLensLinkDirectory(name)),
|
||||||
|
map(ensureDirectory),
|
||||||
|
awaitAll,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import fse from "fs-extra";
|
||||||
|
|
||||||
|
export type CreateSymlink = (target: string, path: string, type: "dir" | "file") => Promise<void>;
|
||||||
|
|
||||||
|
export const createSymlinkInjectable = getInjectable({
|
||||||
|
id: "create-symlink",
|
||||||
|
instantiate: (): CreateSymlink => fse.symlink,
|
||||||
|
});
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import fse from "fs-extra";
|
||||||
|
|
||||||
|
export type EnsureDirectory = (path: string) => Promise<void>;
|
||||||
|
|
||||||
|
export const ensureDirectoryInjectable = getInjectable({
|
||||||
|
id: "ensure-directory",
|
||||||
|
instantiate: (): EnsureDirectory => fse.ensureDir,
|
||||||
|
});
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import fse from "fs-extra";
|
||||||
|
|
||||||
|
export type Exists = (path: string) => Promise<boolean>;
|
||||||
|
|
||||||
|
export const existsInjectable = getInjectable({
|
||||||
|
id: "exists",
|
||||||
|
instantiate: (): Exists => fse.pathExists,
|
||||||
|
});
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import fse from "fs-extra";
|
||||||
|
import type { JsonValue } from "type-fest";
|
||||||
|
|
||||||
|
export type ReadJsonFile = (path: string) => Promise<JsonValue>;
|
||||||
|
|
||||||
|
export const readJsonFileInjectable = getInjectable({
|
||||||
|
id: "read-json-file",
|
||||||
|
instantiate: (): ReadJsonFile => fse.readJson,
|
||||||
|
});
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import fse from "fs-extra";
|
||||||
|
|
||||||
|
export type RemoveDirectory = (path: string) => Promise<void>;
|
||||||
|
|
||||||
|
export const removeDirectoryInjectable = getInjectable({
|
||||||
|
id: "remove-directory",
|
||||||
|
instantiate: (): RemoveDirectory => fse.remove,
|
||||||
|
});
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import fse from "fs-extra";
|
||||||
|
import type { JsonValue } from "type-fest";
|
||||||
|
|
||||||
|
export type WriteJsonFile = (path: string, value: JsonValue) => Promise<void>;
|
||||||
|
|
||||||
|
export const writeJsonFileInjectable = getInjectable({
|
||||||
|
id: "write-json-file",
|
||||||
|
instantiate: (): WriteJsonFile => fse.writeJson,
|
||||||
|
});
|
||||||
14
packages/infrastructure/lens-link/src/get-di.ts
Normal file
14
packages/infrastructure/lens-link/src/get-di.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration";
|
||||||
|
import { createContainer } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
|
export const getDi = () => {
|
||||||
|
const di = createContainer("lens-link");
|
||||||
|
|
||||||
|
autoRegister({
|
||||||
|
di,
|
||||||
|
targetModule: module,
|
||||||
|
getRequireContexts: () => [require.context("./", true, /\.injectable\.(ts|tsx)$/)],
|
||||||
|
});
|
||||||
|
|
||||||
|
return di;
|
||||||
|
};
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { workingDirectoryInjectable } from "./working-directory.injectable";
|
||||||
|
import { resolvePathInjectable } from "./path/resolve-path.injectable";
|
||||||
|
|
||||||
|
export const getLensLinkDirectoryInjectable = getInjectable({
|
||||||
|
id: "get-lens-link-directory",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const resolvePath = di.inject(resolvePathInjectable);
|
||||||
|
const workingDirectory = di.inject(workingDirectoryInjectable);
|
||||||
|
|
||||||
|
return (moduleName: string) => resolvePath(workingDirectory, "node_modules", moduleName);
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import type { ResolvePath } from "./lens-link";
|
|
||||||
|
|
||||||
export type GetLensLinkDirectory = (moduleName: string) => string;
|
|
||||||
|
|
||||||
export const getLensLinkDirectoryFor =
|
|
||||||
(workingDirectory: string, resolvePath: ResolvePath): GetLensLinkDirectory =>
|
|
||||||
(moduleName) =>
|
|
||||||
resolvePath(workingDirectory, "node_modules", moduleName);
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import type { Exists } from "./lens-link";
|
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
|
||||||
import { map, filter } from "lodash/fp";
|
|
||||||
import { awaitAll } from "./await-all";
|
|
||||||
|
|
||||||
export const getMissingPackageJsonsFor = (exists: Exists) => async (packageJsonPaths: string[]) =>
|
|
||||||
pipeline(
|
|
||||||
packageJsonPaths,
|
|
||||||
map(async (packageJsonPath) => ({ packageJsonPath, exists: await exists(packageJsonPath) })),
|
|
||||||
awaitAll,
|
|
||||||
filter(({ exists }) => !exists),
|
|
||||||
map(({ packageJsonPath }) => packageJsonPath),
|
|
||||||
);
|
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
import { map, filter } from "lodash/fp";
|
||||||
|
import { awaitAll } from "./await-all";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { existsInjectable } from "./fs/exists.injectable";
|
||||||
|
|
||||||
|
export const getMissingPackageJsonsInjectable = getInjectable({
|
||||||
|
id: "get-missing-package-jsons",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const exists = di.inject(existsInjectable);
|
||||||
|
|
||||||
|
return async (packageJsonPaths: string[]) =>
|
||||||
|
pipeline(
|
||||||
|
packageJsonPaths,
|
||||||
|
|
||||||
|
map(async (packageJsonPath) => ({
|
||||||
|
packageJsonPath,
|
||||||
|
exists: await exists(packageJsonPath),
|
||||||
|
})),
|
||||||
|
|
||||||
|
awaitAll,
|
||||||
|
filter(({ exists }) => !exists),
|
||||||
|
map(({ packageJsonPath }) => packageJsonPath),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import type { ReadJsonFile, ResolvePath } from "./lens-link";
|
|
||||||
|
|
||||||
export const getPackageJsonPathsFor =
|
|
||||||
({
|
|
||||||
resolvePath,
|
|
||||||
readJsonFile,
|
|
||||||
workingDirectory,
|
|
||||||
}: {
|
|
||||||
workingDirectory: string;
|
|
||||||
resolvePath: ResolvePath;
|
|
||||||
readJsonFile: ReadJsonFile;
|
|
||||||
}) =>
|
|
||||||
async (configFilePath: string) => {
|
|
||||||
const configFile = (await readJsonFile(configFilePath)) as string[];
|
|
||||||
|
|
||||||
return configFile.map((linkPath: string) => resolvePath(workingDirectory, linkPath, "package.json"));
|
|
||||||
};
|
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { resolvePathInjectable } from "./path/resolve-path.injectable";
|
||||||
|
import { workingDirectoryInjectable } from "./working-directory.injectable";
|
||||||
|
import { readJsonFileInjectable } from "./fs/read-json-file.injectable";
|
||||||
|
|
||||||
|
export const getPackageJsonPathsInjectable = getInjectable({
|
||||||
|
id: "get-package-json-paths",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const readJsonFile = di.inject(readJsonFileInjectable);
|
||||||
|
const resolvePath = di.inject(resolvePathInjectable);
|
||||||
|
const workingDirectory = di.inject(workingDirectoryInjectable);
|
||||||
|
|
||||||
|
return async (configFilePath: string) => {
|
||||||
|
const configFile = (await readJsonFile(configFilePath)) as string[];
|
||||||
|
|
||||||
|
return configFile.map((linkPath: string) => resolvePath(workingDirectory, linkPath, "package.json"));
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { awaitAll } from "./await-all";
|
|
||||||
import type { ReadJsonFile } from "./lens-link";
|
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
|
||||||
import { map } from "lodash/fp";
|
|
||||||
import type { PackageJson } from "./package-json-and-path";
|
|
||||||
|
|
||||||
export const getPackageJsonsFor = (readJsonFile: ReadJsonFile) => async (packageJsonPaths: string[]) =>
|
|
||||||
pipeline(
|
|
||||||
packageJsonPaths,
|
|
||||||
|
|
||||||
map(async (packageJsonPath) => ({
|
|
||||||
packageJsonPath,
|
|
||||||
content: (await readJsonFile(packageJsonPath)) as unknown as PackageJson,
|
|
||||||
})),
|
|
||||||
|
|
||||||
awaitAll,
|
|
||||||
);
|
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { awaitAll } from "./await-all";
|
||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
import { map } from "lodash/fp";
|
||||||
|
import type { PackageJson } from "./package-json-and-path";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { readJsonFileInjectable } from "./fs/read-json-file.injectable";
|
||||||
|
|
||||||
|
export const getPackageJsonsInjectable = getInjectable({
|
||||||
|
id: "get-package-jsons",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const readJsonFile = di.inject(readJsonFileInjectable);
|
||||||
|
|
||||||
|
return async (packageJsonPaths: string[]) =>
|
||||||
|
pipeline(
|
||||||
|
packageJsonPaths,
|
||||||
|
|
||||||
|
map(async (packageJsonPath) => ({
|
||||||
|
packageJsonPath,
|
||||||
|
content: (await readJsonFile(packageJsonPath)) as unknown as PackageJson,
|
||||||
|
})),
|
||||||
|
|
||||||
|
awaitAll,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
import { dirname } from "path";
|
||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
import { flatMap, map } from "lodash/fp";
|
||||||
|
import { removeExistingLensLinkDirectoriesInjectable } from "./remove-existing-lens-link-directories.injectable";
|
||||||
|
import { createLensLinkDirectoriesInjectable } from "./create-lens-link-directories.injectable";
|
||||||
|
import { getMissingPackageJsonsInjectable } from "./get-missing-package-jsons.injectable";
|
||||||
|
import { getPackageJsonsInjectable } from "./get-package-jsons.injectable";
|
||||||
|
import { getPackageJsonPathsInjectable } from "./get-package-json-paths.injectable";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { getLensLinkDirectoryInjectable } from "./get-lens-link-directory.injectable";
|
||||||
|
import { resolvePathInjectable } from "./path/resolve-path.injectable";
|
||||||
|
import { existsInjectable } from "./fs/exists.injectable";
|
||||||
|
import { writeJsonFileInjectable } from "./fs/write-json-file.injectable";
|
||||||
|
import { createSymlinkInjectable } from "./fs/create-symlink.injectable";
|
||||||
|
import { workingDirectoryInjectable } from "./working-directory.injectable";
|
||||||
|
|
||||||
|
export type LensLink = () => Promise<void>;
|
||||||
|
|
||||||
|
const lensLinkInjectable = getInjectable({
|
||||||
|
id: "lens-link",
|
||||||
|
|
||||||
|
instantiate: (di): LensLink => {
|
||||||
|
const getPackageJsons = di.inject(getPackageJsonsInjectable);
|
||||||
|
const getLensLinkDirectory = di.inject(getLensLinkDirectoryInjectable);
|
||||||
|
const getMissingPackageJsons = di.inject(getMissingPackageJsonsInjectable);
|
||||||
|
const removeExistingLensLinkDirectories = di.inject(removeExistingLensLinkDirectoriesInjectable);
|
||||||
|
const createLensLinkDirectories = di.inject(createLensLinkDirectoriesInjectable);
|
||||||
|
const getPackageJsonPaths = di.inject(getPackageJsonPathsInjectable);
|
||||||
|
const resolvePath = di.inject(resolvePathInjectable);
|
||||||
|
const exists = di.inject(existsInjectable);
|
||||||
|
const writeJsonFile = di.inject(writeJsonFileInjectable);
|
||||||
|
const createSymlink = di.inject(createSymlinkInjectable);
|
||||||
|
const workingDirectory = di.inject(workingDirectoryInjectable);
|
||||||
|
|
||||||
|
return async () => {
|
||||||
|
const configFilePath = resolvePath(workingDirectory, ".lens-links.json");
|
||||||
|
|
||||||
|
const configFileExists = await exists(configFilePath);
|
||||||
|
|
||||||
|
if (!configFileExists) {
|
||||||
|
await writeJsonFile(configFilePath, []);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageJsonPaths = await getPackageJsonPaths(configFilePath);
|
||||||
|
|
||||||
|
const missingPackageJsons = await getMissingPackageJsons(packageJsonPaths);
|
||||||
|
|
||||||
|
if (missingPackageJsons.length) {
|
||||||
|
throw new Error(
|
||||||
|
`Tried to install Lens links, but configured package.jsons were not found: "${missingPackageJsons.join(
|
||||||
|
'", "',
|
||||||
|
)}".`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageJsons = await getPackageJsons(packageJsonPaths);
|
||||||
|
|
||||||
|
await removeExistingLensLinkDirectories(packageJsons);
|
||||||
|
|
||||||
|
await createLensLinkDirectories(packageJsons);
|
||||||
|
|
||||||
|
pipeline(
|
||||||
|
packageJsons,
|
||||||
|
|
||||||
|
flatMap(({ packageJsonPath, content }) => {
|
||||||
|
const lensLinkDirectory = getLensLinkDirectory(content.name);
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
target: packageJsonPath,
|
||||||
|
source: resolvePath(lensLinkDirectory, "package.json"),
|
||||||
|
type: "file" as const,
|
||||||
|
},
|
||||||
|
|
||||||
|
...content.files.map((x) => ({
|
||||||
|
target: resolvePath(dirname(packageJsonPath), x),
|
||||||
|
source: resolvePath(lensLinkDirectory, x),
|
||||||
|
type: "dir" as const,
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
|
||||||
|
map(({ target, source, type }) => createSymlink(target, source, type)),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default lensLinkInjectable;
|
||||||
@ -1,17 +1,24 @@
|
|||||||
import {
|
import type { LensLink } from "./lens-link.injectable";
|
||||||
EnsureDirectory,
|
|
||||||
CreateSymlink,
|
|
||||||
Exists,
|
|
||||||
lensLinkFor,
|
|
||||||
ReadJsonFile,
|
|
||||||
WriteJsonFile,
|
|
||||||
RemoveDirectory,
|
|
||||||
} from "./lens-link";
|
|
||||||
import type { LensLink } from "./lens-link";
|
|
||||||
import asyncFn from "@async-fn/jest";
|
import asyncFn from "@async-fn/jest";
|
||||||
import type { AsyncFnMock } from "@async-fn/jest";
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
import { getPromiseStatus } from "@k8slens/test-utils";
|
import { getPromiseStatus } from "@k8slens/test-utils";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import lensLinkInjectable from "./lens-link.injectable";
|
||||||
|
import type { Exists } from "./fs/exists.injectable";
|
||||||
|
import type { ReadJsonFile } from "./fs/read-json-file.injectable";
|
||||||
|
import type { WriteJsonFile } from "./fs/write-json-file.injectable";
|
||||||
|
import type { CreateSymlink } from "./fs/create-symlink.injectable";
|
||||||
|
import type { EnsureDirectory } from "./fs/ensure-directory.injectable";
|
||||||
|
import type { RemoveDirectory } from "./fs/remove-directory.injectable";
|
||||||
|
import { workingDirectoryInjectable } from "./working-directory.injectable";
|
||||||
|
import { resolvePathInjectable } from "./path/resolve-path.injectable";
|
||||||
|
import { existsInjectable } from "./fs/exists.injectable";
|
||||||
|
import { readJsonFileInjectable } from "./fs/read-json-file.injectable";
|
||||||
|
import { writeJsonFileInjectable } from "./fs/write-json-file.injectable";
|
||||||
|
import { createSymlinkInjectable } from "./fs/create-symlink.injectable";
|
||||||
|
import { ensureDirectoryInjectable } from "./fs/ensure-directory.injectable";
|
||||||
|
import { removeDirectoryInjectable } from "./fs/remove-directory.injectable";
|
||||||
|
import { getDi } from "./get-di";
|
||||||
|
|
||||||
describe("lens-link", () => {
|
describe("lens-link", () => {
|
||||||
let lensLink: LensLink;
|
let lensLink: LensLink;
|
||||||
@ -30,16 +37,18 @@ describe("lens-link", () => {
|
|||||||
ensureDirectoryMock = asyncFn();
|
ensureDirectoryMock = asyncFn();
|
||||||
removeDirectoryMock = asyncFn();
|
removeDirectoryMock = asyncFn();
|
||||||
|
|
||||||
lensLink = lensLinkFor({
|
const di = getDi();
|
||||||
workingDirectory: "/some-directory/some-project",
|
|
||||||
resolvePath: path.posix.resolve,
|
di.override(workingDirectoryInjectable, () => "/some-directory/some-project");
|
||||||
exists: existsMock,
|
di.override(resolvePathInjectable, () => path.posix.resolve);
|
||||||
readJsonFile: readJsonFileMock,
|
di.override(existsInjectable, () => existsMock);
|
||||||
writeJsonFile: writeJsonFileMock,
|
di.override(readJsonFileInjectable, () => readJsonFileMock);
|
||||||
createSymlink: createSymlinkMock,
|
di.override(writeJsonFileInjectable, () => writeJsonFileMock);
|
||||||
ensureDirectory: ensureDirectoryMock,
|
di.override(createSymlinkInjectable, () => createSymlinkMock);
|
||||||
removeDirectory: removeDirectoryMock,
|
di.override(ensureDirectoryInjectable, () => ensureDirectoryMock);
|
||||||
});
|
di.override(removeDirectoryInjectable, () => removeDirectoryMock);
|
||||||
|
|
||||||
|
lensLink = di.inject(lensLinkInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when called", () => {
|
describe("when called", () => {
|
||||||
@ -265,149 +274,5 @@ describe("lens-link", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
xdescribe("normally", () => {
|
|
||||||
it("checks for existence of package.jsons", () => {
|
|
||||||
expect(existsMock.mock.calls).toEqual([["some-target-directory/package.json"]]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("when package.json for target directory is missing, throws", () => {
|
|
||||||
existsMock.resolveSpecific(
|
|
||||||
([packageJsonPath]) => packageJsonPath === "some-target-directory/package.json",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
existsMock.resolveSpecific(([packageJsonPath]) => packageJsonPath === "some-to-directory/package.json", true);
|
|
||||||
|
|
||||||
return expect(actualPromise).rejects.toThrow(
|
|
||||||
`Tried to link package but package.json is missing in "some-target-directory/package.json"`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("when package.json for to directory is missing, throws", () => {
|
|
||||||
existsMock.resolveSpecific(
|
|
||||||
([packageJsonPath]) => packageJsonPath === "some-target-directory/package.json",
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
existsMock.resolveSpecific(([packageJsonPath]) => packageJsonPath === "some-to-directory/package.json", false);
|
|
||||||
|
|
||||||
return expect(actualPromise).rejects.toThrow(
|
|
||||||
`Tried to link package but package.json is missing in "some-to-directory/package.json"`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("when both package.jsons are missing, throws", () => {
|
|
||||||
existsMock.resolveSpecific(
|
|
||||||
([packageJsonPath]) => packageJsonPath === "some-target-directory/package.json",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
existsMock.resolveSpecific(([packageJsonPath]) => packageJsonPath === "some-to-directory/package.json", false);
|
|
||||||
|
|
||||||
return expect(actualPromise).rejects.toThrow(
|
|
||||||
`Tried to link package but package.jsons are missing in "some-target-directory/package.json", "some-to-directory/package.json"`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("when check for package.jsons resolves with both existing", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await existsMock.resolve(true);
|
|
||||||
await existsMock.resolve(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("reads the target package json", () => {
|
|
||||||
expect(readJsonFileMock).toHaveBeenCalledWith("some-target-directory/package.json");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not read the to package json", () => {
|
|
||||||
expect(readJsonFileMock).not.toHaveBeenCalledWith("some-to-directory/package.json");
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("when package json resolves with enough data", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
existsMock.mockClear();
|
|
||||||
|
|
||||||
await readJsonFileMock.resolve({
|
|
||||||
name: "some-npm-package",
|
|
||||||
main: "some-directory-in-package/some-entrypoint.js",
|
|
||||||
files: ["some-directory-in-package", "some-other-directory-in-package"],
|
|
||||||
|
|
||||||
irrelevant: "property",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("checks for existence of target npm package in node modules of the to-package", () => {
|
|
||||||
expect(existsMock).toHaveBeenCalledWith("some-to-directory/node_modules/some-npm-package");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("when check for existence of target npm package in node_modules resolves with existing directory, throws", () => {
|
|
||||||
existsMock.resolve(true);
|
|
||||||
|
|
||||||
// Note, this shouldn't throw, it should ask whether user wants to override existing
|
|
||||||
return expect(actualPromise).rejects.toThrow("Asd");
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("when check for existence of target npm package in node_modules resolves with non existing directory", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await existsMock.resolve(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("ensures directory for the target npm package in node modules", () => {
|
|
||||||
expect(ensureDirectoryMock).toHaveBeenCalledWith("some-to-directory/node_modules/some-npm-package");
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("when ensuring directory resolves", () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await ensureDirectoryMock.resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates minimal package.json in the node_modules", () => {
|
|
||||||
expect(writeJsonFileMock).toHaveBeenCalledWith(
|
|
||||||
"some-to-directory/node_modules/some-npm-package/package.json",
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "some-npm-package",
|
|
||||||
main: "some-directory-in-package/some-entrypoint.js",
|
|
||||||
files: ["some-directory-in-package", "some-other-directory-in-package"],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("creates the symlinks for the files or directories in package.json", () => {
|
|
||||||
expect(createSymlinkMock.mock.calls).toEqual([
|
|
||||||
[
|
|
||||||
"some-target-directory/some-directory-in-package",
|
|
||||||
"some-to-directory/node_modules/some-npm-package/some-directory-in-package",
|
|
||||||
"dir",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"some-target-directory/some-other-directory-in-package",
|
|
||||||
"some-to-directory/node_modules/some-npm-package/some-other-directory-in-package",
|
|
||||||
"dir",
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not resolve yet", async () => {
|
|
||||||
const promiseStatus = await getPromiseStatus(actualPromise);
|
|
||||||
|
|
||||||
expect(promiseStatus.fulfilled).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("when creation files and symlinks resolve, resolves", async () => {
|
|
||||||
writeJsonFileMock.resolve();
|
|
||||||
createSymlinkMock.resolve();
|
|
||||||
createSymlinkMock.resolve();
|
|
||||||
|
|
||||||
const promiseStatus = await getPromiseStatus(actualPromise);
|
|
||||||
|
|
||||||
expect(promiseStatus.fulfilled).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,133 +0,0 @@
|
|||||||
import type { JsonValue } from "type-fest";
|
|
||||||
import fse from "fs-extra";
|
|
||||||
import { dirname, resolve as _resolvePath } from "path";
|
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
|
||||||
import { flatMap, map } from "lodash/fp";
|
|
||||||
import { getLensLinkDirectoryFor } from "./get-lens-link-directory";
|
|
||||||
import { removeExistingLensLinkDirectoriesFor } from "./remove-existing-lens-link-directories-for";
|
|
||||||
import { createLensLinkDirectoriesFor } from "./create-lens-link-directories-for";
|
|
||||||
import { getMissingPackageJsonsFor } from "./get-missing-package-jsons-for";
|
|
||||||
import { getPackageJsonsFor } from "./get-package-jsons-for";
|
|
||||||
import { getPackageJsonPathsFor } from "./get-package-json-paths-for";
|
|
||||||
|
|
||||||
export type LensLink = () => Promise<void>;
|
|
||||||
|
|
||||||
export type CreateSymlink = (target: string, path: string, type: "dir" | "file") => Promise<void>;
|
|
||||||
export type EnsureDirectory = (path: string) => Promise<void>;
|
|
||||||
export type RemoveDirectory = (path: string) => Promise<void>;
|
|
||||||
export type WriteJsonFile = (path: string, value: JsonValue) => Promise<void>;
|
|
||||||
export type ReadJsonFile = (path: string) => Promise<JsonValue>;
|
|
||||||
export type Exists = (path: string) => Promise<boolean>;
|
|
||||||
export type ResolvePath = typeof _resolvePath;
|
|
||||||
|
|
||||||
interface Dependencies {
|
|
||||||
workingDirectory: string;
|
|
||||||
resolvePath: ResolvePath;
|
|
||||||
exists: Exists;
|
|
||||||
readJsonFile: ReadJsonFile;
|
|
||||||
writeJsonFile: WriteJsonFile;
|
|
||||||
createSymlink: CreateSymlink;
|
|
||||||
ensureDirectory: EnsureDirectory;
|
|
||||||
removeDirectory: RemoveDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
const existsAsd = fse.pathExists;
|
|
||||||
const readJsonFileAsd = fse.readJson;
|
|
||||||
const writeJsonFileAsd = fse.writeJson;
|
|
||||||
const createSymlinkAsd = fse.symlink;
|
|
||||||
const ensureDirectoryAsd = fse.ensureDir;
|
|
||||||
const removeDirectoryAsd = fse.remove;
|
|
||||||
|
|
||||||
export const lensLinkFor =
|
|
||||||
(
|
|
||||||
{
|
|
||||||
workingDirectory,
|
|
||||||
resolvePath,
|
|
||||||
exists,
|
|
||||||
readJsonFile,
|
|
||||||
writeJsonFile,
|
|
||||||
createSymlink,
|
|
||||||
ensureDirectory,
|
|
||||||
removeDirectory,
|
|
||||||
}: Dependencies = {
|
|
||||||
workingDirectory: process.cwd(),
|
|
||||||
resolvePath: _resolvePath,
|
|
||||||
exists: existsAsd,
|
|
||||||
readJsonFile: readJsonFileAsd,
|
|
||||||
writeJsonFile: writeJsonFileAsd,
|
|
||||||
createSymlink: createSymlinkAsd,
|
|
||||||
ensureDirectory: ensureDirectoryAsd,
|
|
||||||
removeDirectory: removeDirectoryAsd,
|
|
||||||
},
|
|
||||||
): LensLink =>
|
|
||||||
async () => {
|
|
||||||
const getPackageJsons = getPackageJsonsFor(readJsonFile);
|
|
||||||
const getLensLinkDirectory = getLensLinkDirectoryFor(workingDirectory, resolvePath);
|
|
||||||
const getMissingPackageJsons = getMissingPackageJsonsFor(exists);
|
|
||||||
|
|
||||||
const removeExistingLensLinkDirectories = removeExistingLensLinkDirectoriesFor(
|
|
||||||
getLensLinkDirectory,
|
|
||||||
exists,
|
|
||||||
removeDirectory,
|
|
||||||
);
|
|
||||||
|
|
||||||
const createLensLinkDirectories = createLensLinkDirectoriesFor(getLensLinkDirectory, ensureDirectory);
|
|
||||||
|
|
||||||
const getPackageJsonPaths = getPackageJsonPathsFor({
|
|
||||||
workingDirectory,
|
|
||||||
resolvePath,
|
|
||||||
readJsonFile,
|
|
||||||
});
|
|
||||||
|
|
||||||
const configFilePath = resolvePath(workingDirectory, ".lens-links.json");
|
|
||||||
|
|
||||||
const configFileExists = await exists(configFilePath);
|
|
||||||
|
|
||||||
if (!configFileExists) {
|
|
||||||
await writeJsonFile(configFilePath, []);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const packageJsonPaths = await getPackageJsonPaths(configFilePath);
|
|
||||||
|
|
||||||
const missingPackageJsons = await getMissingPackageJsons(packageJsonPaths);
|
|
||||||
|
|
||||||
if (missingPackageJsons.length) {
|
|
||||||
throw new Error(
|
|
||||||
`Tried to install Lens links, but configured package.jsons were not found: "${missingPackageJsons.join(
|
|
||||||
'", "',
|
|
||||||
)}".`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const packageJsons = await getPackageJsons(packageJsonPaths);
|
|
||||||
|
|
||||||
await removeExistingLensLinkDirectories(packageJsons);
|
|
||||||
|
|
||||||
await createLensLinkDirectories(packageJsons);
|
|
||||||
|
|
||||||
pipeline(
|
|
||||||
packageJsons,
|
|
||||||
|
|
||||||
flatMap(({ packageJsonPath, content }) => {
|
|
||||||
const lensLinkDirectory = getLensLinkDirectory(content.name);
|
|
||||||
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
target: packageJsonPath,
|
|
||||||
source: resolvePath(lensLinkDirectory, "package.json"),
|
|
||||||
type: "file" as const,
|
|
||||||
},
|
|
||||||
|
|
||||||
...content.files.map((x) => ({
|
|
||||||
target: resolvePath(dirname(packageJsonPath), x),
|
|
||||||
source: resolvePath(lensLinkDirectory, x),
|
|
||||||
type: "dir" as const,
|
|
||||||
})),
|
|
||||||
];
|
|
||||||
}),
|
|
||||||
|
|
||||||
map(({ target, source, type }) => createSymlink(target, source, type)),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export type ResolvePath = typeof path.resolve;
|
||||||
|
|
||||||
|
export const resolvePathInjectable = getInjectable({
|
||||||
|
id: "resolve-path",
|
||||||
|
instantiate: (): ResolvePath => path.resolve,
|
||||||
|
});
|
||||||
@ -1,37 +0,0 @@
|
|||||||
import type { GetLensLinkDirectory } from "./get-lens-link-directory";
|
|
||||||
import type { Exists, RemoveDirectory } from "./lens-link";
|
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
|
||||||
import { map, filter } from "lodash/fp";
|
|
||||||
import type { PackageJsonAndPath } from "./ensure-lens-link-directories";
|
|
||||||
import { awaitAll } from "./await-all";
|
|
||||||
|
|
||||||
export type RemoveExistingLensLinkDirectories = (packageJsons: PackageJsonAndPath[]) => Promise<void>;
|
|
||||||
|
|
||||||
export const removeExistingLensLinkDirectoriesFor =
|
|
||||||
(
|
|
||||||
getLensLinkDirectory: GetLensLinkDirectory,
|
|
||||||
exists: Exists,
|
|
||||||
removeDirectory: RemoveDirectory,
|
|
||||||
): RemoveExistingLensLinkDirectories =>
|
|
||||||
async (packageJsons) => {
|
|
||||||
await pipeline(
|
|
||||||
packageJsons,
|
|
||||||
|
|
||||||
map(async ({ content }) => {
|
|
||||||
const lensLinkDirectory = getLensLinkDirectory(content.name);
|
|
||||||
|
|
||||||
return {
|
|
||||||
directory: lensLinkDirectory,
|
|
||||||
exists: await exists(lensLinkDirectory),
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
|
|
||||||
awaitAll,
|
|
||||||
|
|
||||||
filter(({ exists }) => exists),
|
|
||||||
|
|
||||||
map(({ directory }) => removeDirectory(directory)),
|
|
||||||
|
|
||||||
awaitAll,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
import { map, filter } from "lodash/fp";
|
||||||
|
import { awaitAll } from "./await-all";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { getLensLinkDirectoryInjectable } from "./get-lens-link-directory.injectable";
|
||||||
|
import { existsInjectable } from "./fs/exists.injectable";
|
||||||
|
import { removeDirectoryInjectable } from "./fs/remove-directory.injectable";
|
||||||
|
import type { PackageJsonAndPath } from "./package-json-and-path";
|
||||||
|
|
||||||
|
export type RemoveExistingLensLinkDirectories = (packageJsons: PackageJsonAndPath[]) => Promise<void>;
|
||||||
|
|
||||||
|
export const removeExistingLensLinkDirectoriesInjectable = getInjectable({
|
||||||
|
id: "remove-existing-lens-link-directories",
|
||||||
|
|
||||||
|
instantiate: (di): RemoveExistingLensLinkDirectories => {
|
||||||
|
const getLensLinkDirectory = di.inject(getLensLinkDirectoryInjectable);
|
||||||
|
const exists = di.inject(existsInjectable);
|
||||||
|
const removeDirectory = di.inject(removeDirectoryInjectable);
|
||||||
|
|
||||||
|
return async (packageJsons) => {
|
||||||
|
await pipeline(
|
||||||
|
packageJsons,
|
||||||
|
|
||||||
|
map(async ({ content }) => {
|
||||||
|
const lensLinkDirectory = getLensLinkDirectory(content.name);
|
||||||
|
|
||||||
|
return {
|
||||||
|
directory: lensLinkDirectory,
|
||||||
|
exists: await exists(lensLinkDirectory),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
awaitAll,
|
||||||
|
|
||||||
|
filter(({ exists }) => exists),
|
||||||
|
|
||||||
|
map(({ directory }) => removeDirectory(directory)),
|
||||||
|
|
||||||
|
awaitAll,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
|
export const workingDirectoryInjectable = getInjectable({
|
||||||
|
id: "working-directory",
|
||||||
|
instantiate: () => process.cwd(),
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user