Implement --revert for own command
This commit is contained in:
parent
6de5fd4f96
commit
d2da43c617
@ -263,6 +263,7 @@ program
|
||||
program
|
||||
.command<{
|
||||
path: string;
|
||||
revert: boolean;
|
||||
}>({
|
||||
name: "own",
|
||||
description: [
|
||||
@ -286,14 +287,26 @@ program
|
||||
"Example `--path admin/page/Login.tsx`"
|
||||
].join(" ")
|
||||
})
|
||||
.option({
|
||||
key: "revert",
|
||||
name: (() => {
|
||||
const name = "revert";
|
||||
|
||||
optionsKeys.push(name);
|
||||
|
||||
return name;
|
||||
})(),
|
||||
description: "Revert ownership claim over a given file or directory.",
|
||||
defaultValue: false
|
||||
})
|
||||
.task({
|
||||
skip,
|
||||
handler: async ({ projectDirPath, path }) => {
|
||||
handler: async ({ projectDirPath, path, revert }) => {
|
||||
const { command } = await import("./own");
|
||||
|
||||
await command({
|
||||
buildContext: getBuildContext({ projectDirPath }),
|
||||
cliCommandOptions: { path }
|
||||
cliCommandOptions: { path, isRevert: revert }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
182
src/bin/own.ts
182
src/bin/own.ts
@ -11,58 +11,109 @@ import {
|
||||
} from "./postinstall/managedGitignoreFile";
|
||||
import { isInside } from "./tools/isInside";
|
||||
import chalk from "chalk";
|
||||
import type { UiModuleMeta } from "./postinstall/uiModuleMeta";
|
||||
import { command as command_postinstall } from "./postinstall";
|
||||
|
||||
export async function command(params: {
|
||||
buildContext: BuildContext;
|
||||
cliCommandOptions: {
|
||||
path: string;
|
||||
isRevert: boolean;
|
||||
};
|
||||
}) {
|
||||
const { buildContext, cliCommandOptions } = params;
|
||||
|
||||
const fileOrDirectoryRelativePath = pathRelative(
|
||||
buildContext.themeSrcDirPath,
|
||||
getAbsoluteAndInOsFormatPath({
|
||||
cwd: buildContext.themeSrcDirPath,
|
||||
pathIsh: cliCommandOptions.path
|
||||
})
|
||||
);
|
||||
|
||||
const uiModuleMetas = await getUiModuleMetas({ buildContext });
|
||||
|
||||
const arr = uiModuleMetas
|
||||
.map(uiModuleMeta => ({
|
||||
uiModuleMeta,
|
||||
fileRelativePaths: uiModuleMeta.files
|
||||
.map(({ fileRelativePath }) => fileRelativePath)
|
||||
.filter(
|
||||
fileRelativePath =>
|
||||
fileRelativePath === fileOrDirectoryRelativePath ||
|
||||
isInside({
|
||||
dirPath: fileOrDirectoryRelativePath,
|
||||
filePath: fileRelativePath
|
||||
})
|
||||
)
|
||||
}))
|
||||
.filter(({ fileRelativePaths }) => fileRelativePaths.length !== 0);
|
||||
const { targetFileRelativePathsByUiModuleMeta } = await (async () => {
|
||||
const fileOrDirectoryRelativePath = pathRelative(
|
||||
buildContext.themeSrcDirPath,
|
||||
getAbsoluteAndInOsFormatPath({
|
||||
cwd: buildContext.themeSrcDirPath,
|
||||
pathIsh: cliCommandOptions.path
|
||||
})
|
||||
);
|
||||
|
||||
if (arr.length === 0) {
|
||||
const arr = uiModuleMetas
|
||||
.map(uiModuleMeta => ({
|
||||
uiModuleMeta,
|
||||
fileRelativePaths: uiModuleMeta.files
|
||||
.map(({ fileRelativePath }) => fileRelativePath)
|
||||
.filter(
|
||||
fileRelativePath =>
|
||||
fileRelativePath === fileOrDirectoryRelativePath ||
|
||||
isInside({
|
||||
dirPath: fileOrDirectoryRelativePath,
|
||||
filePath: fileRelativePath
|
||||
})
|
||||
)
|
||||
}))
|
||||
.filter(({ fileRelativePaths }) => fileRelativePaths.length !== 0);
|
||||
|
||||
const targetFileRelativePathsByUiModuleMeta = new Map<UiModuleMeta, string[]>();
|
||||
|
||||
for (const { uiModuleMeta, fileRelativePaths } of arr) {
|
||||
targetFileRelativePathsByUiModuleMeta.set(uiModuleMeta, fileRelativePaths);
|
||||
}
|
||||
|
||||
return { targetFileRelativePathsByUiModuleMeta };
|
||||
})();
|
||||
|
||||
if (targetFileRelativePathsByUiModuleMeta.size === 0) {
|
||||
console.log(
|
||||
chalk.yellow("There is no UI module files matching the provided path.")
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { ownedFilesRelativePaths: ownedFilesRelativePaths_before } =
|
||||
const { ownedFilesRelativePaths: ownedFilesRelativePaths_current } =
|
||||
await readManagedGitignoreFile({
|
||||
buildContext
|
||||
});
|
||||
|
||||
const ownedFilesRelativePaths_toAdd: string[] = [];
|
||||
await (cliCommandOptions.isRevert ? command_revert : command_own)({
|
||||
uiModuleMetas,
|
||||
targetFileRelativePathsByUiModuleMeta,
|
||||
ownedFilesRelativePaths_current,
|
||||
buildContext
|
||||
});
|
||||
}
|
||||
|
||||
type Params_subcommands = {
|
||||
uiModuleMetas: UiModuleMeta[];
|
||||
targetFileRelativePathsByUiModuleMeta: Map<UiModuleMeta, string[]>;
|
||||
ownedFilesRelativePaths_current: string[];
|
||||
buildContext: BuildContext;
|
||||
};
|
||||
|
||||
async function command_own(params: Params_subcommands) {
|
||||
const {
|
||||
uiModuleMetas,
|
||||
targetFileRelativePathsByUiModuleMeta,
|
||||
ownedFilesRelativePaths_current,
|
||||
buildContext
|
||||
} = params;
|
||||
|
||||
await writeManagedGitignoreFile({
|
||||
buildContext,
|
||||
uiModuleMetas,
|
||||
ownedFilesRelativePaths: [
|
||||
...ownedFilesRelativePaths_current,
|
||||
...Array.from(targetFileRelativePathsByUiModuleMeta.values())
|
||||
.flat()
|
||||
.filter(
|
||||
fileRelativePath =>
|
||||
!ownedFilesRelativePaths_current.includes(fileRelativePath)
|
||||
)
|
||||
]
|
||||
});
|
||||
|
||||
const writeActions: (() => Promise<void>)[] = [];
|
||||
|
||||
for (const { uiModuleMeta, fileRelativePaths } of arr) {
|
||||
for (const [
|
||||
uiModuleMeta,
|
||||
fileRelativePaths
|
||||
] of targetFileRelativePathsByUiModuleMeta.entries()) {
|
||||
const uiModuleDirPath = await getInstalledModuleDirPath({
|
||||
moduleName: uiModuleMeta.moduleName,
|
||||
packageJsonDirPath: pathDirname(buildContext.packageJsonFilePath),
|
||||
@ -70,46 +121,81 @@ export async function command(params: {
|
||||
});
|
||||
|
||||
for (const fileRelativePath of fileRelativePaths) {
|
||||
if (ownedFilesRelativePaths_before.includes(fileRelativePath)) {
|
||||
if (ownedFilesRelativePaths_current.includes(fileRelativePath)) {
|
||||
console.log(
|
||||
chalk.yellow(`You already have ownership over "${fileRelativePath}".`)
|
||||
chalk.grey(`You already have ownership over '${fileRelativePath}'.`)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const sourceCode = await getUiModuleFileSourceCodeReadyToBeCopied({
|
||||
buildContext,
|
||||
fileRelativePath,
|
||||
isOwnershipAction: true,
|
||||
uiModuleName: uiModuleMeta.moduleName,
|
||||
uiModuleDirPath,
|
||||
uiModuleVersion: uiModuleMeta.version
|
||||
});
|
||||
writeActions.push(async () => {
|
||||
const sourceCode = await getUiModuleFileSourceCodeReadyToBeCopied({
|
||||
buildContext,
|
||||
fileRelativePath,
|
||||
isOwnershipAction: true,
|
||||
uiModuleName: uiModuleMeta.moduleName,
|
||||
uiModuleDirPath,
|
||||
uiModuleVersion: uiModuleMeta.version
|
||||
});
|
||||
|
||||
writeActions.push(() =>
|
||||
fsPr.writeFile(
|
||||
await fsPr.writeFile(
|
||||
pathJoin(buildContext.themeSrcDirPath, fileRelativePath),
|
||||
sourceCode
|
||||
)
|
||||
);
|
||||
);
|
||||
|
||||
ownedFilesRelativePaths_toAdd.push(fileRelativePath);
|
||||
console.log(chalk.green(`Ownership over '${fileRelativePath}' claimed.`));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (ownedFilesRelativePaths_toAdd.length === 0) {
|
||||
if (writeActions.length === 0) {
|
||||
console.log(chalk.yellow("No new file claimed."));
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(writeActions.map(action => action()));
|
||||
}
|
||||
|
||||
async function command_revert(params: Params_subcommands) {
|
||||
const {
|
||||
uiModuleMetas,
|
||||
targetFileRelativePathsByUiModuleMeta,
|
||||
ownedFilesRelativePaths_current,
|
||||
buildContext
|
||||
} = params;
|
||||
|
||||
const ownedFilesRelativePaths_toRemove = Array.from(
|
||||
targetFileRelativePathsByUiModuleMeta.values()
|
||||
)
|
||||
.flat()
|
||||
.filter(fileRelativePath => {
|
||||
if (!ownedFilesRelativePaths_current.includes(fileRelativePath)) {
|
||||
console.log(
|
||||
chalk.grey(`Ownership over '${fileRelativePath}' wasn't claimed.`)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(
|
||||
chalk.green(`Ownership over '${fileRelativePath}' relinquished.`)
|
||||
);
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (ownedFilesRelativePaths_toRemove.length === 0) {
|
||||
console.log(chalk.yellow("No file relinquished."));
|
||||
return;
|
||||
}
|
||||
|
||||
await writeManagedGitignoreFile({
|
||||
buildContext,
|
||||
uiModuleMetas,
|
||||
ownedFilesRelativePaths: [
|
||||
...ownedFilesRelativePaths_before,
|
||||
...ownedFilesRelativePaths_toAdd
|
||||
]
|
||||
ownedFilesRelativePaths: ownedFilesRelativePaths_current.filter(
|
||||
fileRelativePath =>
|
||||
!ownedFilesRelativePaths_toRemove.includes(fileRelativePath)
|
||||
)
|
||||
});
|
||||
|
||||
await command_postinstall({ buildContext });
|
||||
}
|
||||
|
@ -35,18 +35,25 @@ export async function getUiModuleFileSourceCodeReadyToBeCopied(params: {
|
||||
sourceCode = addCommentToSourceCode({
|
||||
sourceCode,
|
||||
fileRelativePath,
|
||||
commentLines: isOwnershipAction
|
||||
? [
|
||||
`This file was claimed for ownership from ${uiModuleName} version ${uiModuleVersion}.`
|
||||
]
|
||||
: [
|
||||
`WARNING: Before modifying this file run the following command:`,
|
||||
``,
|
||||
`$ npx keycloakify own --path '${fileRelativePath.split(pathSep).join("/")}'`,
|
||||
``,
|
||||
`This file comes from ${uiModuleName} version ${uiModuleVersion}.`,
|
||||
`This file has been copied over to your repo by your postinstall script: \`npx keycloakify postinstall\``
|
||||
]
|
||||
commentLines: (() => {
|
||||
const path = fileRelativePath.split(pathSep).join("/");
|
||||
|
||||
return isOwnershipAction
|
||||
? [
|
||||
`This file has been claimed for ownership from ${uiModuleName} version ${uiModuleVersion}.`,
|
||||
`To relinquish ownership and restore this file to its original content, run the following command:`,
|
||||
``,
|
||||
`$ npx keycloakify own --revert --path '${path}'`
|
||||
]
|
||||
: [
|
||||
`WARNING: Before modifying this file, run the following command:`,
|
||||
``,
|
||||
`$ npx keycloakify own --path '${path}'`,
|
||||
``,
|
||||
`This file is provided by ${uiModuleName} version ${uiModuleVersion}.`,
|
||||
`It was copied into your repository by the postinstall script: \`keycloakify postinstall\`.`
|
||||
];
|
||||
})()
|
||||
});
|
||||
|
||||
const destFilePath = pathJoin(buildContext.themeSrcDirPath, fileRelativePath);
|
||||
|
@ -9,7 +9,7 @@ import { dirname as pathDirname } from "path";
|
||||
import { join as pathJoin } from "path";
|
||||
import { existsAsync } from "../tools/fs.existsAsync";
|
||||
import * as fsPr from "fs/promises";
|
||||
import { getIsTrackedByGit } from "../tools/isTrackedByGit";
|
||||
import { getIsKnownByGit } from "../tools/isKnownByGit";
|
||||
import { untrackFromGit } from "../tools/untrackFromGit";
|
||||
|
||||
export async function command(params: { buildContext: BuildContext }) {
|
||||
@ -65,19 +65,7 @@ export async function command(params: { buildContext: BuildContext }) {
|
||||
return;
|
||||
}
|
||||
|
||||
git_untrack: {
|
||||
if (!doesFileExist) {
|
||||
break git_untrack;
|
||||
}
|
||||
|
||||
const isTracked = await getIsTrackedByGit({
|
||||
filePath: destFilePath
|
||||
});
|
||||
|
||||
if (!isTracked) {
|
||||
break git_untrack;
|
||||
}
|
||||
|
||||
if (await getIsKnownByGit({ filePath: destFilePath })) {
|
||||
await untrackFromGit({
|
||||
filePath: destFilePath
|
||||
});
|
||||
|
@ -2,28 +2,28 @@ import * as child_process from "child_process";
|
||||
import { dirname as pathDirname, basename as pathBasename } from "path";
|
||||
import { Deferred } from "evt/tools/Deferred";
|
||||
|
||||
export function getIsTrackedByGit(params: { filePath: string }): Promise<boolean> {
|
||||
export function getIsKnownByGit(params: { filePath: string }): Promise<boolean> {
|
||||
const { filePath } = params;
|
||||
|
||||
const dIsTracked = new Deferred<boolean>();
|
||||
const dIsKnownByGit = new Deferred<boolean>();
|
||||
|
||||
child_process.exec(
|
||||
`git ls-files --error-unmatch ${pathBasename(filePath)}`,
|
||||
{ cwd: pathDirname(filePath) },
|
||||
error => {
|
||||
if (error === null) {
|
||||
dIsTracked.resolve(true);
|
||||
dIsKnownByGit.resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (error.code === 1) {
|
||||
dIsTracked.resolve(false);
|
||||
dIsKnownByGit.resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
dIsTracked.reject(error);
|
||||
dIsKnownByGit.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
return dIsTracked.pr;
|
||||
return dIsKnownByGit.pr;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user