@oclif/plugin-command-snapshot 4.0.14 → 4.0.16-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/lib/commands/schema/compare.d.ts +5 -5
- package/lib/commands/schema/compare.js +49 -46
- package/lib/commands/schema/generate.d.ts +6 -6
- package/lib/commands/schema/generate.js +79 -79
- package/lib/commands/snapshot/compare.d.ts +13 -13
- package/lib/commands/snapshot/compare.js +45 -47
- package/lib/commands/snapshot/generate.d.ts +2 -2
- package/lib/commands/snapshot/generate.js +19 -17
- package/lib/index.js +1 -3
- package/lib/snapshot-command.d.ts +6 -5
- package/lib/snapshot-command.js +24 -20
- package/lib/util.js +3 -12
- package/oclif.lock +7020 -0
- package/oclif.manifest.json +107 -47
- package/package.json +46 -31
- package/schemas/schema-compare.json +4 -15
- package/schemas/snapshot-compare.json +35 -47
- package/schemas/snapshot-generate.json +11 -18
package/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
@oclif/plugin-command-snapshot
|
|
2
|
-
========
|
|
1
|
+
# @oclif/plugin-command-snapshot
|
|
3
2
|
|
|
4
3
|
Generates and compares OCLIF plugins snapshot files
|
|
5
4
|
|
|
@@ -9,12 +8,14 @@ Generates and compares OCLIF plugins snapshot files
|
|
|
9
8
|
[](https://github.com/nramyasri-sf/@oclif/plugin-command-snapshot/blob/main/package.json)
|
|
10
9
|
|
|
11
10
|
<!-- toc -->
|
|
11
|
+
* [@oclif/plugin-command-snapshot](#oclifplugin-command-snapshot)
|
|
12
12
|
* [Usage](#usage)
|
|
13
13
|
* [generates a snapshot file that has a list or commands and flags in the current CLI or plugin](#generates-a-snapshot-file-that-has-a-list-or-commands-and-flags-in-the-current-cli-or-plugin)
|
|
14
14
|
* [compare the current CLI or plugin commands and flags with a snapshot file to identify changes](#compare-the-current-cli-or-plugin-commands-and-flags-with-a-snapshot-file-to-identify-changes)
|
|
15
15
|
<!-- tocstop -->
|
|
16
16
|
|
|
17
17
|
# Usage
|
|
18
|
+
|
|
18
19
|
This plugin is used to take snapshot of commands and flags in OCLIF plugins. These snapshots can be used to enforce a deprecation policy or to keep track of changes.
|
|
19
20
|
|
|
20
21
|
To include it in your plugin, add it to the `devPlugins` section in your package.json.
|
|
@@ -32,12 +33,12 @@ Use the `snapshot:generate` command in your development process.
|
|
|
32
33
|
|
|
33
34
|
```sh-session
|
|
34
35
|
# generates a snapshot file that has a list or commands and flags in the current CLI or plugin
|
|
35
|
-
$ ./bin/run snapshot:generate
|
|
36
|
+
$ ./bin/run.js snapshot:generate
|
|
36
37
|
```
|
|
37
38
|
|
|
38
39
|
Use the `snapshot:compare` command in your continuos integration. This command will fail if changes are detected. The snapshot file should be updated and committed to the repository when legitimate changes are made.
|
|
39
40
|
|
|
40
41
|
```sh-session
|
|
41
42
|
# compare the current CLI or plugin commands and flags with a snapshot file to identify changes
|
|
42
|
-
$ ./bin/run snapshot:compare
|
|
43
|
+
$ ./bin/run.js snapshot:compare
|
|
43
44
|
```
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Operation } from 'just-diff';
|
|
2
|
-
import
|
|
2
|
+
import SnapshotCommand from '../../snapshot-command.js';
|
|
3
3
|
export type SchemaComparison = Array<{
|
|
4
4
|
op: Operation;
|
|
5
|
-
path: (
|
|
6
|
-
value:
|
|
5
|
+
path: (number | string)[];
|
|
6
|
+
value: unknown;
|
|
7
7
|
}>;
|
|
8
8
|
export default class SchemaCompare extends SnapshotCommand {
|
|
9
9
|
static flags: {
|
|
10
|
-
filepath: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
|
+
filepath: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
11
11
|
};
|
|
12
12
|
run(): Promise<SchemaComparison>;
|
|
13
|
-
private readExistingSchema;
|
|
14
13
|
private generateLatestSchema;
|
|
14
|
+
private readExistingSchema;
|
|
15
15
|
}
|
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const chalk_1 = require("chalk");
|
|
12
|
-
const util_1 = require("../../util");
|
|
1
|
+
import { Flags, toConfiguredId } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { diff } from 'just-diff';
|
|
4
|
+
import get from 'lodash.get';
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import * as semver from 'semver';
|
|
8
|
+
import SnapshotCommand from '../../snapshot-command.js';
|
|
9
|
+
import { getKeyNameFromFilename } from '../../util.js';
|
|
10
|
+
import { SchemaGenerator, getAllFiles } from './generate.js';
|
|
13
11
|
function isNumber(n) {
|
|
14
12
|
return Number.isInteger(Number(n));
|
|
15
13
|
}
|
|
16
14
|
function isMeaningless(n) {
|
|
15
|
+
if (!n)
|
|
16
|
+
return true;
|
|
17
17
|
const meaninglessKeys = ['$comment', '__computed'];
|
|
18
18
|
return meaninglessKeys.includes(n);
|
|
19
19
|
}
|
|
20
|
-
class SchemaCompare extends
|
|
20
|
+
export default class SchemaCompare extends SnapshotCommand {
|
|
21
|
+
static flags = {
|
|
22
|
+
filepath: Flags.string({
|
|
23
|
+
default: './schemas',
|
|
24
|
+
description: 'path of the generated snapshot file',
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
21
27
|
async run() {
|
|
22
28
|
const { flags } = await this.parse(SchemaCompare);
|
|
23
29
|
try {
|
|
@@ -28,18 +34,18 @@ class SchemaCompare extends snapshot_command_1.SnapshotCommand {
|
|
|
28
34
|
return [];
|
|
29
35
|
}
|
|
30
36
|
const existingSchema = this.readExistingSchema(flags.filepath);
|
|
31
|
-
const latestSchema =
|
|
37
|
+
const latestSchema = await this.generateLatestSchema();
|
|
32
38
|
this.debug('existingSchema', existingSchema);
|
|
33
39
|
this.debug('latestSchema', latestSchema);
|
|
34
|
-
const changes =
|
|
40
|
+
const changes = diff(latestSchema, existingSchema);
|
|
35
41
|
const humanReadableChanges = {};
|
|
36
42
|
for (const change of changes) {
|
|
37
|
-
const lastPathElement = change.path
|
|
43
|
+
const lastPathElement = change.path.at(-1);
|
|
38
44
|
if (isMeaningless(lastPathElement))
|
|
39
45
|
continue;
|
|
40
46
|
const objPath = change.path.join('.');
|
|
41
|
-
const existing =
|
|
42
|
-
const latest =
|
|
47
|
+
const existing = get(existingSchema, objPath);
|
|
48
|
+
const latest = get(latestSchema, objPath);
|
|
43
49
|
const [commandId] = objPath.split('.definitions');
|
|
44
50
|
const readablePath = objPath.replace(`${commandId}.`, '');
|
|
45
51
|
if (!humanReadableChanges[commandId]) {
|
|
@@ -48,21 +54,25 @@ class SchemaCompare extends snapshot_command_1.SnapshotCommand {
|
|
|
48
54
|
const lastElementIsNum = isNumber(lastPathElement);
|
|
49
55
|
const basePath = lastElementIsNum ? readablePath.replace(`.${lastPathElement}`, '') : readablePath;
|
|
50
56
|
switch (change.op) {
|
|
51
|
-
case 'replace':
|
|
52
|
-
humanReadableChanges[commandId].push(`${
|
|
57
|
+
case 'replace': {
|
|
58
|
+
humanReadableChanges[commandId].push(`${chalk.underline(readablePath)} was changed from ${chalk.cyan(existing)} to ${chalk.cyan(latest)}`);
|
|
53
59
|
break;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
}
|
|
61
|
+
case 'add': {
|
|
62
|
+
humanReadableChanges[commandId].push(lastElementIsNum
|
|
63
|
+
? `Array item at ${chalk.underline(basePath)} was ${chalk.cyan('added')} to latest schema`
|
|
64
|
+
: `${chalk.underline(readablePath)} was ${chalk.cyan('added')} to latest schema`);
|
|
58
65
|
break;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
}
|
|
67
|
+
case 'remove': {
|
|
68
|
+
humanReadableChanges[commandId].push(lastElementIsNum
|
|
69
|
+
? `Array item at ${chalk.underline(basePath)} was ${chalk.cyan('not found')} in latest schema`
|
|
70
|
+
: `${chalk.underline(readablePath)} was ${chalk.cyan('not found')} in latest schema`);
|
|
63
71
|
break;
|
|
64
|
-
|
|
72
|
+
}
|
|
73
|
+
default: {
|
|
65
74
|
break;
|
|
75
|
+
}
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
if (Object.keys(humanReadableChanges).length === 0) {
|
|
@@ -70,25 +80,29 @@ class SchemaCompare extends snapshot_command_1.SnapshotCommand {
|
|
|
70
80
|
return [];
|
|
71
81
|
}
|
|
72
82
|
this.log();
|
|
73
|
-
this.log(
|
|
83
|
+
this.log(chalk.bold.red('Found the following schema changes:'));
|
|
74
84
|
for (const [commandId, changes] of Object.entries(humanReadableChanges)) {
|
|
75
85
|
this.log();
|
|
76
|
-
this.log(
|
|
86
|
+
this.log(chalk.bold(commandId));
|
|
77
87
|
for (const change of changes) {
|
|
78
88
|
this.log(` - ${change}`);
|
|
79
89
|
}
|
|
80
90
|
}
|
|
81
91
|
this.log();
|
|
82
92
|
const bin = process.platform === 'win32' ? 'bin\\dev.cmd' : 'bin/dev';
|
|
83
|
-
this.log('If intended, please update the schema file(s) and run again:',
|
|
93
|
+
this.log('If intended, please update the schema file(s) and run again:', chalk.bold(`${bin} ${toConfiguredId('schema:generate', this.config)}`));
|
|
84
94
|
process.exitCode = 1;
|
|
85
95
|
return changes;
|
|
86
96
|
}
|
|
97
|
+
async generateLatestSchema() {
|
|
98
|
+
const generator = new SchemaGenerator(this);
|
|
99
|
+
return generator.generate();
|
|
100
|
+
}
|
|
87
101
|
readExistingSchema(filePath) {
|
|
88
102
|
const contents = fs.readdirSync(filePath);
|
|
89
|
-
const folderIsVersioned = contents.every(c => semver.valid(c));
|
|
103
|
+
const folderIsVersioned = contents.every((c) => semver.valid(c));
|
|
90
104
|
const schemasDir = folderIsVersioned ? path.join(filePath, semver.rsort(contents)[0] || '') : filePath;
|
|
91
|
-
const schemaFiles =
|
|
105
|
+
const schemaFiles = getAllFiles(schemasDir, '.json');
|
|
92
106
|
let schemas = {
|
|
93
107
|
commands: {},
|
|
94
108
|
hooks: {},
|
|
@@ -99,7 +113,7 @@ class SchemaCompare extends snapshot_command_1.SnapshotCommand {
|
|
|
99
113
|
else {
|
|
100
114
|
for (const file of schemaFiles) {
|
|
101
115
|
const schema = JSON.parse(fs.readFileSync(file).toString('utf8'));
|
|
102
|
-
const key = path.basename(
|
|
116
|
+
const key = path.basename(getKeyNameFromFilename(file));
|
|
103
117
|
if (file.split(path.sep).includes('hooks')) {
|
|
104
118
|
schemas.hooks[key] = schema;
|
|
105
119
|
}
|
|
@@ -110,15 +124,4 @@ class SchemaCompare extends snapshot_command_1.SnapshotCommand {
|
|
|
110
124
|
}
|
|
111
125
|
return schemas;
|
|
112
126
|
}
|
|
113
|
-
async generateLatestSchema() {
|
|
114
|
-
const generator = new generate_1.SchemaGenerator(this);
|
|
115
|
-
return generator.generate();
|
|
116
|
-
}
|
|
117
127
|
}
|
|
118
|
-
exports.default = SchemaCompare;
|
|
119
|
-
SchemaCompare.flags = {
|
|
120
|
-
filepath: core_1.Flags.string({
|
|
121
|
-
description: 'path of the generated snapshot file',
|
|
122
|
-
default: './schemas',
|
|
123
|
-
}),
|
|
124
|
-
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema } from 'ts-json-schema-generator';
|
|
2
|
-
import
|
|
2
|
+
import SnapshotCommand from '../../snapshot-command.js';
|
|
3
3
|
export type SchemasMap = {
|
|
4
4
|
[key: string]: Schema;
|
|
5
5
|
};
|
|
@@ -15,9 +15,11 @@ export declare class SchemaGenerator {
|
|
|
15
15
|
private classToId;
|
|
16
16
|
constructor(base: SnapshotCommand, ignoreVoid?: boolean);
|
|
17
17
|
generate(): Promise<Schemas>;
|
|
18
|
+
private determineCommandId;
|
|
18
19
|
private generateSchema;
|
|
19
20
|
private getAllCmdFiles;
|
|
20
21
|
private getAllHookFiles;
|
|
22
|
+
private getDirs;
|
|
21
23
|
/**
|
|
22
24
|
* Use regex to find the typescript type being returned by the command's
|
|
23
25
|
* `run` method.
|
|
@@ -32,14 +34,12 @@ export declare class SchemaGenerator {
|
|
|
32
34
|
*/
|
|
33
35
|
private parseHookFile;
|
|
34
36
|
private validateReturnType;
|
|
35
|
-
private determineCommandId;
|
|
36
|
-
private getDirs;
|
|
37
37
|
}
|
|
38
38
|
export default class SchemaGenerate extends SnapshotCommand {
|
|
39
39
|
static flags: {
|
|
40
|
-
filepath: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
filepath: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
41
|
+
ignorevoid: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
42
|
+
singlefile: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
43
43
|
};
|
|
44
44
|
run(): Promise<GenerateResponse>;
|
|
45
45
|
}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const chalk_1 = require("chalk");
|
|
10
|
-
const util_1 = require("../../util");
|
|
11
|
-
function getAllFiles(dirPath, ext, allFiles = []) {
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { createGenerator } from 'ts-json-schema-generator';
|
|
6
|
+
import SnapshotCommand from '../../snapshot-command.js';
|
|
7
|
+
import { getSchemaFileName } from '../../util.js';
|
|
8
|
+
export function getAllFiles(dirPath, ext, allFiles = []) {
|
|
12
9
|
let files = [];
|
|
13
10
|
try {
|
|
14
11
|
files = fs.readdirSync(dirPath);
|
|
@@ -25,12 +22,14 @@ function getAllFiles(dirPath, ext, allFiles = []) {
|
|
|
25
22
|
}
|
|
26
23
|
return allFiles;
|
|
27
24
|
}
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
export class SchemaGenerator {
|
|
26
|
+
base;
|
|
27
|
+
ignoreVoid;
|
|
28
|
+
classToId = {};
|
|
29
|
+
// eslint-disable-next-line no-useless-constructor
|
|
30
30
|
constructor(base, ignoreVoid = true) {
|
|
31
31
|
this.base = base;
|
|
32
32
|
this.ignoreVoid = ignoreVoid;
|
|
33
|
-
this.classToId = {};
|
|
34
33
|
}
|
|
35
34
|
async generate() {
|
|
36
35
|
for (const cmd of this.base.commands) {
|
|
@@ -40,32 +39,45 @@ class SchemaGenerator {
|
|
|
40
39
|
}
|
|
41
40
|
const cmdSchemas = {};
|
|
42
41
|
for (const file of this.getAllCmdFiles()) {
|
|
43
|
-
const {
|
|
42
|
+
const { commandId, returnType } = this.parseCmdFile(file);
|
|
44
43
|
if (this.ignoreVoid && returnType === 'void')
|
|
45
44
|
continue;
|
|
46
45
|
cmdSchemas[commandId] = this.generateSchema(returnType, file);
|
|
47
46
|
}
|
|
48
47
|
const hookSchemas = {};
|
|
49
48
|
for (const file of this.getAllHookFiles()) {
|
|
50
|
-
const {
|
|
49
|
+
const { hookId, returnType } = this.parseHookFile(file);
|
|
51
50
|
if (returnType && hookId) {
|
|
52
51
|
hookSchemas[hookId] = this.generateSchema(returnType, file);
|
|
53
52
|
}
|
|
54
53
|
}
|
|
55
54
|
return { commands: cmdSchemas, hooks: hookSchemas };
|
|
56
55
|
}
|
|
56
|
+
determineCommandId(contents) {
|
|
57
|
+
for (const [className, cmdId] of Object.entries(this.classToId)) {
|
|
58
|
+
const regex = new RegExp(` class ${className} `, 'g');
|
|
59
|
+
if (regex.test(contents)) {
|
|
60
|
+
return cmdId;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
57
64
|
generateSchema(returnType, file) {
|
|
58
65
|
try {
|
|
59
66
|
const config = {
|
|
60
67
|
path: file,
|
|
61
|
-
type: returnType,
|
|
62
68
|
skipTypeCheck: true,
|
|
69
|
+
type: returnType,
|
|
63
70
|
};
|
|
64
|
-
return
|
|
71
|
+
return createGenerator(config).createSchema(config.type);
|
|
65
72
|
}
|
|
66
73
|
catch (error) {
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
if (error instanceof Error) {
|
|
75
|
+
const error_ = error.message.toLowerCase().includes('no root type')
|
|
76
|
+
? new Error(`Schema generator could not find the ${chalk.red(returnType)} type. Please make sure that ${chalk.red(returnType)} is exported.`)
|
|
77
|
+
: error;
|
|
78
|
+
throw error_;
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
getAllCmdFiles() {
|
|
@@ -74,11 +86,27 @@ class SchemaGenerator {
|
|
|
74
86
|
}
|
|
75
87
|
getAllHookFiles() {
|
|
76
88
|
// eslint-disable-next-line unicorn/no-array-reduce
|
|
77
|
-
const hookFiles = Object.values(this.base.config.pjson.oclif?.hooks ?? {}).reduce((x, y) =>
|
|
78
|
-
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
const hookFiles = Object.values(this.base.config.pjson.oclif?.hooks ?? {}).reduce((x, y) => (Array.isArray(y) ? [...x, ...y] : [...x, y]), []);
|
|
90
|
+
const { outDir, rootDir } = this.getDirs();
|
|
91
|
+
return hookFiles.map((h) => `${path.resolve(h)}.ts`.replace(outDir, rootDir));
|
|
92
|
+
}
|
|
93
|
+
getDirs() {
|
|
94
|
+
const dirs = {
|
|
95
|
+
outDir: path.join(this.base.config.root, 'lib'),
|
|
96
|
+
rootDir: path.join(this.base.config.root, 'src'),
|
|
97
|
+
};
|
|
98
|
+
try {
|
|
99
|
+
const tsConfig = JSON.parse(fs.readFileSync(path.join(this.base.config.root, 'tsconfig.json'), 'utf8'));
|
|
100
|
+
if (tsConfig.compilerOptions.rootDir) {
|
|
101
|
+
dirs.rootDir = path.join(this.base.config.root, tsConfig.compilerOptions.rootDir);
|
|
102
|
+
}
|
|
103
|
+
if (tsConfig.compilerOptions.outDir) {
|
|
104
|
+
dirs.outDir = path.join(this.base.config.root, tsConfig.compilerOptions.outDir);
|
|
105
|
+
}
|
|
106
|
+
return dirs;
|
|
107
|
+
}
|
|
108
|
+
catch { }
|
|
109
|
+
return dirs;
|
|
82
110
|
}
|
|
83
111
|
/**
|
|
84
112
|
* Use regex to find the typescript type being returned by the command's
|
|
@@ -98,7 +126,7 @@ class SchemaGenerator {
|
|
|
98
126
|
throw new Error(`No commandId found for file ${file}`);
|
|
99
127
|
}
|
|
100
128
|
this.validateReturnType(returnType, commandId);
|
|
101
|
-
return {
|
|
129
|
+
return { commandId, returnType };
|
|
102
130
|
}
|
|
103
131
|
/**
|
|
104
132
|
* Use regex to find the typescript type being returned by the hook
|
|
@@ -110,20 +138,20 @@ class SchemaGenerator {
|
|
|
110
138
|
const contents = fs.readFileSync(file, 'utf8');
|
|
111
139
|
const [returnType] = returnTypeRegex.exec(contents) || [];
|
|
112
140
|
if (!returnType || returnType === 'void') {
|
|
113
|
-
return {
|
|
141
|
+
return { hookId: null, returnType: null };
|
|
114
142
|
}
|
|
115
143
|
const hooks = this.base.config.pjson.oclif?.hooks ?? {};
|
|
116
|
-
const hookId = Object.keys(hooks).find(key => {
|
|
144
|
+
const hookId = Object.keys(hooks).find((key) => {
|
|
117
145
|
const hookFiles = (Array.isArray(hooks[key]) ? hooks[key] : [hooks[key]]);
|
|
118
|
-
const hookFileNames = hookFiles.map(f => path.basename(f).split('.')[0]);
|
|
146
|
+
const hookFileNames = hookFiles.map((f) => path.basename(f).split('.')[0]);
|
|
119
147
|
const currentFileName = path.basename(file).split('.')[0];
|
|
120
148
|
return hookFileNames.includes(currentFileName);
|
|
121
149
|
});
|
|
122
150
|
if (!hookId) {
|
|
123
|
-
return {
|
|
151
|
+
return { hookId: null, returnType: null };
|
|
124
152
|
}
|
|
125
153
|
this.validateReturnType(returnType, hookId);
|
|
126
|
-
return {
|
|
154
|
+
return { hookId, returnType };
|
|
127
155
|
}
|
|
128
156
|
validateReturnType(returnType, commandId) {
|
|
129
157
|
const notAllowed = this.ignoreVoid ? ['any', 'unknown'] : ['any', 'unknown', 'void'];
|
|
@@ -135,35 +163,22 @@ class SchemaGenerator {
|
|
|
135
163
|
throw new Error(`${returnType} (from ${commandId}) is too vague. Please use a more specific type.`);
|
|
136
164
|
}
|
|
137
165
|
}
|
|
138
|
-
determineCommandId(contents) {
|
|
139
|
-
for (const [className, cmdId] of Object.entries(this.classToId)) {
|
|
140
|
-
const regex = new RegExp(` class ${className} `, 'g');
|
|
141
|
-
if (regex.test(contents)) {
|
|
142
|
-
return cmdId;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
getDirs() {
|
|
147
|
-
const dirs = {
|
|
148
|
-
rootDir: path.join(this.base.config.root, 'src'),
|
|
149
|
-
outDir: path.join(this.base.config.root, 'lib'),
|
|
150
|
-
};
|
|
151
|
-
try {
|
|
152
|
-
const tsConfig = JSON.parse(fs.readFileSync(path.join(this.base.config.root, 'tsconfig.json'), 'utf-8'));
|
|
153
|
-
if (tsConfig.compilerOptions.rootDir) {
|
|
154
|
-
dirs.rootDir = path.join(this.base.config.root, tsConfig.compilerOptions.rootDir);
|
|
155
|
-
}
|
|
156
|
-
if (tsConfig.compilerOptions.outDir) {
|
|
157
|
-
dirs.outDir = path.join(this.base.config.root, tsConfig.compilerOptions.outDir);
|
|
158
|
-
}
|
|
159
|
-
return dirs;
|
|
160
|
-
}
|
|
161
|
-
catch { }
|
|
162
|
-
return dirs;
|
|
163
|
-
}
|
|
164
166
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
+
export default class SchemaGenerate extends SnapshotCommand {
|
|
168
|
+
static flags = {
|
|
169
|
+
filepath: Flags.string({
|
|
170
|
+
default: './schemas',
|
|
171
|
+
description: 'directory to save the generated schema files; can use "{version}" to insert the current CLI/plugin version',
|
|
172
|
+
}),
|
|
173
|
+
ignorevoid: Flags.boolean({
|
|
174
|
+
default: true,
|
|
175
|
+
description: 'ignore commands that return void',
|
|
176
|
+
}),
|
|
177
|
+
singlefile: Flags.boolean({
|
|
178
|
+
default: false,
|
|
179
|
+
description: 'put generated schema into a single file',
|
|
180
|
+
}),
|
|
181
|
+
};
|
|
167
182
|
async run() {
|
|
168
183
|
const { flags } = await this.parse(SchemaGenerate);
|
|
169
184
|
const generator = new SchemaGenerator(this, flags.ignorevoid);
|
|
@@ -173,15 +188,15 @@ class SchemaGenerate extends snapshot_command_1.SnapshotCommand {
|
|
|
173
188
|
const files = [];
|
|
174
189
|
if (flags.singlefile) {
|
|
175
190
|
const filePath = path.join(directory, 'schema.json');
|
|
176
|
-
|
|
191
|
+
this.write(filePath, JSON.stringify(schemas, null, 2));
|
|
177
192
|
this.log(`Generated JSON schema file "${filePath}"`);
|
|
178
193
|
files.push(filePath);
|
|
179
194
|
}
|
|
180
195
|
else {
|
|
181
196
|
for (const [cmdId, schema] of Object.entries(schemas.commands)) {
|
|
182
|
-
const fileName =
|
|
197
|
+
const fileName = getSchemaFileName(cmdId);
|
|
183
198
|
const filePath = path.join(directory, fileName);
|
|
184
|
-
|
|
199
|
+
this.write(filePath, JSON.stringify(schema, null, 2));
|
|
185
200
|
this.log(`Generated JSON schema file "${filePath}"`);
|
|
186
201
|
files.push(filePath);
|
|
187
202
|
}
|
|
@@ -189,9 +204,9 @@ class SchemaGenerate extends snapshot_command_1.SnapshotCommand {
|
|
|
189
204
|
const hooksDir = path.join(directory, 'hooks');
|
|
190
205
|
fs.mkdirSync(hooksDir, { recursive: true });
|
|
191
206
|
for (const [hookId, schema] of Object.entries(schemas.hooks)) {
|
|
192
|
-
const fileName =
|
|
207
|
+
const fileName = getSchemaFileName(hookId);
|
|
193
208
|
const filePath = path.join(hooksDir, fileName);
|
|
194
|
-
|
|
209
|
+
this.write(filePath, JSON.stringify(schema, null, 2));
|
|
195
210
|
this.log(`Generated JSON schema file "${filePath}"`);
|
|
196
211
|
files.push(filePath);
|
|
197
212
|
}
|
|
@@ -200,18 +215,3 @@ class SchemaGenerate extends snapshot_command_1.SnapshotCommand {
|
|
|
200
215
|
return files;
|
|
201
216
|
}
|
|
202
217
|
}
|
|
203
|
-
exports.default = SchemaGenerate;
|
|
204
|
-
SchemaGenerate.flags = {
|
|
205
|
-
filepath: core_1.Flags.string({
|
|
206
|
-
description: 'directory to save the generated schema files; can use "{version}" to insert the current CLI/plugin version',
|
|
207
|
-
default: './schemas',
|
|
208
|
-
}),
|
|
209
|
-
singlefile: core_1.Flags.boolean({
|
|
210
|
-
description: 'put generated schema into a single file',
|
|
211
|
-
default: false,
|
|
212
|
-
}),
|
|
213
|
-
ignorevoid: core_1.Flags.boolean({
|
|
214
|
-
description: 'ignore commands that return void',
|
|
215
|
-
default: true,
|
|
216
|
-
}),
|
|
217
|
-
};
|
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
import
|
|
1
|
+
import SnapshotCommand, { SnapshotEntry } from '../../snapshot-command.js';
|
|
2
2
|
interface Change {
|
|
3
|
+
added?: boolean;
|
|
3
4
|
name: string;
|
|
4
5
|
removed?: boolean;
|
|
5
|
-
added?: boolean;
|
|
6
6
|
}
|
|
7
7
|
type CommandChange = {
|
|
8
|
-
|
|
8
|
+
alias: Change[];
|
|
9
9
|
chars: Change[];
|
|
10
10
|
flags: Change[];
|
|
11
|
-
|
|
11
|
+
plugin: string;
|
|
12
12
|
} & Change;
|
|
13
13
|
export type CompareResponse = {
|
|
14
14
|
addedCommands?: string[];
|
|
15
|
+
diffCommands?: CommandChange[];
|
|
15
16
|
removedCommands?: string[];
|
|
16
17
|
removedFlags?: string[];
|
|
17
|
-
diffCommands?: CommandChange[];
|
|
18
18
|
};
|
|
19
19
|
export default class Compare extends SnapshotCommand {
|
|
20
20
|
static flags: {
|
|
21
|
-
filepath: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
21
|
+
filepath: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
22
22
|
};
|
|
23
|
+
get changed(): CommandChange[];
|
|
23
24
|
/**
|
|
24
25
|
* Compare a snapshot with the current commands
|
|
25
26
|
* @param {CommandChange[]} initialCommands Command list from the snapshot
|
|
@@ -28,18 +29,17 @@ export default class Compare extends SnapshotCommand {
|
|
|
28
29
|
*/
|
|
29
30
|
compareSnapshot(initialCommands: SnapshotEntry[], updatedCommands: CommandChange[]): Promise<CompareResponse>;
|
|
30
31
|
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
* compares two command's properties to each other
|
|
33
|
+
* @return a list of added, removed, updated, and changed properties
|
|
34
|
+
* @param initial initial command property to compare against
|
|
35
|
+
* @param updated generated command property to compare with
|
|
36
|
+
*/
|
|
36
37
|
diffCommandProperty(initial: string[], updated: Change[]): {
|
|
37
38
|
addedProperty: string[];
|
|
39
|
+
changedProperty: Change[];
|
|
38
40
|
removedProperty: string[];
|
|
39
41
|
updated: Change[];
|
|
40
|
-
changedProperty: Change[];
|
|
41
42
|
};
|
|
42
|
-
get changed(): CommandChange[];
|
|
43
43
|
run(): Promise<CompareResponse>;
|
|
44
44
|
}
|
|
45
45
|
export {};
|