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