packages/cli: functional diff command with prompt for updates

This commit is contained in:
Patrik Oldsberg
2020-05-03 13:44:24 +02:00
parent 323f6b715a
commit 8ebb45e990
+137 -17
View File
@@ -15,13 +15,14 @@
*/
import fs from 'fs-extra';
import { relative as relativePath } from 'path';
import { relative as relativePath, dirname } from 'path';
import { diffLines } from 'diff';
import handlebars, { Template } from 'handlebars';
import handlebars from 'handlebars';
import recursiveReadDir from 'recursive-readdir';
import { paths } from 'lib/paths';
import { version } from 'lib/version';
import chalk from 'chalk';
import inquirer from 'inquirer';
type PluginInfo = {
id: string;
@@ -123,29 +124,138 @@ async function readTemplate(
return templateFiles;
}
async function packageJsonHandler(file: TemplateFile) {
if (!file.targetExists) {
throw new Error(`${file.targetPath} doesn't exist`);
async function writeTargetFile(targetPath: string, contents: string) {
const path = paths.resolveTarget(targetPath);
await fs.ensureDir(dirname(path));
await fs.writeFile(path, contents, 'utf8');
}
class PackageJsonHandler {
static async handler(file: TemplateFile) {
console.log('Checking package.json');
if (!file.targetExists) {
throw new Error(`${file.targetPath} doesn't exist`);
}
const pkg = JSON.parse(file.templateContents);
const targetPkg = JSON.parse(file.targetContents);
const handler = new PackageJsonHandler(file, pkg, targetPkg);
await handler.handle();
}
console.log(`pkg.json handler: ${file.targetPath}`);
const pkg = JSON.parse(file.templateContents);
console.log('DEBUG: pkg =', pkg);
const targetPkg = JSON.parse(file.targetContents);
console.log('DEBUG: targetPkg =', targetPkg);
private changed: boolean = false;
constructor(
private readonly file: TemplateFile,
private readonly pkg: any,
private readonly targetPkg: any,
) {}
async handle() {
await this.syncField('main');
await this.syncField('types');
await this.syncField('files');
await this.syncScripts();
await this.syncDependencies('dependencies');
await this.syncDependencies('devDependencies');
}
private async syncField(
fieldName: string,
obj: any = this.pkg,
targetObj: any = this.targetPkg,
prefix?: string,
) {
const fullFieldName = chalk.cyan(
prefix ? `${prefix}[${fieldName}]` : prefix,
);
const newValue = obj[fieldName];
if (fieldName in targetObj) {
const oldValue = targetObj[fieldName];
if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
return;
}
const { addField } = await inquirer.prompt({
type: 'confirm',
name: 'addField',
message: chalk.blue(
`Outdated field, ${fullFieldName}, change from ` +
`${chalk.cyan(oldValue)} to ${chalk.cyan(newValue)}?`,
),
});
if (addField) {
targetObj[fieldName] = newValue;
await this.write();
}
} else {
const { updateField } = await inquirer.prompt({
type: 'confirm',
name: 'updateField',
message: chalk.blue(
`Missing field ${fullFieldName}, set to ${chalk.cyan(newValue)}?`,
),
});
if (updateField) {
targetObj[fieldName] = newValue;
await this.write();
}
}
}
private async syncScripts() {
const pkgScripts = this.pkg.scripts;
const targetScripts = (this.targetPkg.scripts =
this.targetPkg.scripts || {});
for (const key of Object.keys(pkgScripts)) {
await this.syncField(key, pkgScripts, targetScripts, 'scripts');
}
}
private async syncDependencies(fieldName: string) {
const pkgDeps = this.pkg[fieldName];
const targetDeps = (this.targetPkg[fieldName] =
this.targetPkg[fieldName] || {});
for (const key of Object.keys(pkgDeps)) {
await this.syncField(key, pkgDeps, targetDeps, fieldName);
}
}
private async write() {
await fs.writeFile(
paths.resolveTarget(this.file.targetPath),
JSON.stringify(this.targetPkg, null, 2),
);
}
}
async function diffHandler(file: TemplateFile) {
console.log(`Checking ${file.targetPath}`);
const { targetPath, templateContents } = file;
const coloredPath = chalk.cyan(targetPath);
if (!file.targetExists) {
// TODO: prompt to write template file
const { addFile } = await inquirer.prompt({
type: 'confirm',
name: 'addFile',
message: chalk.blue(`Missing ${coloredPath}, do you want to add it?`),
});
if (addFile) {
await writeTargetFile(targetPath, templateContents);
}
return;
}
if (file.targetContents === file.templateContents) {
if (file.targetContents === templateContents) {
return;
}
const diffs = diffLines(file.targetContents, file.templateContents);
const diffs = diffLines(file.targetContents, templateContents);
for (const diff of diffs) {
if (diff.added) {
process.stdout.write(chalk.green(`+${diff.value}`));
@@ -155,6 +265,18 @@ async function diffHandler(file: TemplateFile) {
process.stdout.write(` ${diff.value}`);
}
}
const { applyPatch } = await inquirer.prompt({
type: 'confirm',
name: 'applyPatch',
message: chalk.blue(
`Outdated ${coloredPath}, do you want to apply the above patch?`,
),
});
if (applyPatch) {
await writeTargetFile(targetPath, templateContents);
}
}
async function skipHandler(file: TemplateFile) {
@@ -169,7 +291,7 @@ type FileHandler = {
const fileHandlers: FileHandler[] = [
{
patterns: ['package.json'],
handler: packageJsonHandler,
handler: PackageJsonHandler.handler,
},
{
patterns: ['.eslintrc.js', 'tsconfig.json'],
@@ -204,6 +326,4 @@ export default async () => {
throw new Error(`No template file handler found for ${targetPath}`);
}
}
console.log(`DEBUG: done!`);
};