@oclif/plugin-command-snapshot 3.0.0 → 3.1.0
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/CHANGELOG.md +13 -0
- package/lib/commands/schema/compare.js +16 -5
- package/lib/commands/schema/generate.d.ts +22 -2
- package/lib/commands/schema/generate.js +119 -32
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
- package/yarn.lock +23 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [3.1.0](https://github.com/oclif/plugin-command-snapshot/compare/v3.0.0...v3.1.0) (2021-10-08)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* generate schema for hooks ([266b832](https://github.com/oclif/plugin-command-snapshot/commit/266b83242fab1961b036998119791b999a08ee19))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* ignore hooks with no return type ([070ed41](https://github.com/oclif/plugin-command-snapshot/commit/070ed41902268d48034fdaa2b96f9a4a54eefd2c))
|
|
16
|
+
* schema:compare with hooks ([508549a](https://github.com/oclif/plugin-command-snapshot/commit/508549a372e56b3d79dec970e62477d9905dec13))
|
|
17
|
+
|
|
5
18
|
### [2.2.2](https://github.com/oclif/plugin-command-snapshot/compare/v2.2.1...v2.2.2) (2021-06-30)
|
|
6
19
|
|
|
7
20
|
|
|
@@ -20,7 +20,9 @@ class SchemaCompare extends snapshot_command_1.SnapshotCommand {
|
|
|
20
20
|
return [];
|
|
21
21
|
}
|
|
22
22
|
const existingSchema = this.readExistingSchema(flags.filepath);
|
|
23
|
-
const latestSchema = await this.generateLatestSchema();
|
|
23
|
+
const latestSchema = (await this.generateLatestSchema());
|
|
24
|
+
this.debug('existingSchema', existingSchema);
|
|
25
|
+
this.debug('latestSchema', latestSchema);
|
|
24
26
|
const changes = just_diff_1.diff(latestSchema, existingSchema);
|
|
25
27
|
if (changes.length === 0) {
|
|
26
28
|
this.log('No changes have been detected.');
|
|
@@ -44,7 +46,7 @@ class SchemaCompare extends snapshot_command_1.SnapshotCommand {
|
|
|
44
46
|
humandReadableChanges[commandId].push(`${chalk_1.underline(readablePath)} was ${chalk_1.cyan('added')} to latest schema`);
|
|
45
47
|
break;
|
|
46
48
|
case 'remove':
|
|
47
|
-
humandReadableChanges[commandId].push(`${chalk_1.underline(readablePath)} was ${chalk_1.cyan('
|
|
49
|
+
humandReadableChanges[commandId].push(`${chalk_1.underline(readablePath)} was ${chalk_1.cyan('not found')} in latest schema`);
|
|
48
50
|
break;
|
|
49
51
|
default:
|
|
50
52
|
break;
|
|
@@ -68,15 +70,24 @@ class SchemaCompare extends snapshot_command_1.SnapshotCommand {
|
|
|
68
70
|
const contents = fs.readdirSync(filePath);
|
|
69
71
|
const folderIsVersioned = contents.every(c => semver.valid(c));
|
|
70
72
|
const schemasDir = folderIsVersioned ? path.join(filePath, semver.rsort(contents)[0] || '') : filePath;
|
|
71
|
-
const schemaFiles =
|
|
72
|
-
let schemas = {
|
|
73
|
+
const schemaFiles = generate_1.getAllFiles(schemasDir, '.json');
|
|
74
|
+
let schemas = {
|
|
75
|
+
commands: {},
|
|
76
|
+
hooks: {},
|
|
77
|
+
};
|
|
73
78
|
if (schemaFiles.length === 1 && schemaFiles[0].endsWith('schema.json')) {
|
|
74
79
|
schemas = JSON.parse(fs.readFileSync(schemaFiles[0]).toString('utf8'));
|
|
75
80
|
}
|
|
76
81
|
else {
|
|
77
82
|
for (const file of schemaFiles) {
|
|
78
83
|
const schema = JSON.parse(fs.readFileSync(file).toString('utf8'));
|
|
79
|
-
|
|
84
|
+
const key = path.basename(file.replace(/-/g, ':')).replace('.json', '');
|
|
85
|
+
if (file.split(path.sep).includes('hooks')) {
|
|
86
|
+
schemas.hooks[key] = schema;
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
schemas.commands[key] = schema;
|
|
90
|
+
}
|
|
80
91
|
}
|
|
81
92
|
}
|
|
82
93
|
return schemas;
|
|
@@ -3,17 +3,37 @@ import { SnapshotCommand } from '../../snapshot-command';
|
|
|
3
3
|
export declare type SchemasMap = {
|
|
4
4
|
[key: string]: Schema;
|
|
5
5
|
};
|
|
6
|
+
export declare type Schemas = {
|
|
7
|
+
commands: SchemasMap;
|
|
8
|
+
hooks: SchemasMap;
|
|
9
|
+
};
|
|
6
10
|
export declare type GenerateResponse = string[];
|
|
11
|
+
export declare function getAllFiles(dirPath: string, ext: string, allFiles?: string[]): string[];
|
|
7
12
|
export declare class SchemaGenerator {
|
|
8
13
|
private base;
|
|
9
14
|
private ignorevoid;
|
|
10
15
|
private classToId;
|
|
11
16
|
constructor(base: SnapshotCommand, ignorevoid?: boolean);
|
|
12
|
-
generate(): Promise<
|
|
17
|
+
generate(): Promise<Schemas>;
|
|
18
|
+
private generateSchema;
|
|
13
19
|
private getAllCmdFiles;
|
|
14
|
-
private
|
|
20
|
+
private getAllHookFiles;
|
|
21
|
+
/**
|
|
22
|
+
* Use regex to find the typescript type being returned by the command's
|
|
23
|
+
* `run` method.
|
|
24
|
+
* @param file the file to parse
|
|
25
|
+
* @returns Returns the name of the return type and the command id.
|
|
26
|
+
*/
|
|
27
|
+
private parseCmdFile;
|
|
28
|
+
/**
|
|
29
|
+
* Use regex to find the typescript type being returned by the hook
|
|
30
|
+
* @param file the file to parse
|
|
31
|
+
* @returns Returns the name of the return type and the hook id.
|
|
32
|
+
*/
|
|
33
|
+
private parseHookFile;
|
|
15
34
|
private validateReturnType;
|
|
16
35
|
private determineCommandId;
|
|
36
|
+
private getDirs;
|
|
17
37
|
}
|
|
18
38
|
export default class SchemaGenerate extends SnapshotCommand {
|
|
19
39
|
static flags: {
|
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SchemaGenerator = void 0;
|
|
3
|
+
exports.SchemaGenerator = exports.getAllFiles = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const core_1 = require("@oclif/core");
|
|
7
7
|
const ts_json_schema_generator_1 = require("ts-json-schema-generator");
|
|
8
8
|
const snapshot_command_1 = require("../../snapshot-command");
|
|
9
9
|
const chalk_1 = require("chalk");
|
|
10
|
+
function getAllFiles(dirPath, ext, allFiles = []) {
|
|
11
|
+
let files = [];
|
|
12
|
+
try {
|
|
13
|
+
files = fs.readdirSync(dirPath);
|
|
14
|
+
}
|
|
15
|
+
catch (_a) { }
|
|
16
|
+
files.forEach(file => {
|
|
17
|
+
const fPath = path.join(dirPath, file);
|
|
18
|
+
if (fs.statSync(fPath).isDirectory()) {
|
|
19
|
+
allFiles = getAllFiles(fPath, ext, allFiles);
|
|
20
|
+
}
|
|
21
|
+
else if (file.endsWith(ext)) {
|
|
22
|
+
allFiles.push(fPath);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return allFiles;
|
|
26
|
+
}
|
|
27
|
+
exports.getAllFiles = getAllFiles;
|
|
10
28
|
class SchemaGenerator {
|
|
11
29
|
constructor(base, ignorevoid = true) {
|
|
12
30
|
this.base = base;
|
|
@@ -19,45 +37,59 @@ class SchemaGenerator {
|
|
|
19
37
|
const loadedCmd = await cmd.load(); // commands are loaded async in oclif/core
|
|
20
38
|
this.classToId[loadedCmd.name] = loadedCmd.id;
|
|
21
39
|
}
|
|
22
|
-
const
|
|
40
|
+
const cmdSchemas = {};
|
|
23
41
|
for (const file of this.getAllCmdFiles()) {
|
|
24
|
-
const { returnType, commandId } = this.
|
|
42
|
+
const { returnType, commandId } = this.parseCmdFile(file);
|
|
25
43
|
if (this.ignorevoid && returnType === 'void')
|
|
26
44
|
continue;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
schemas[commandId] = schema;
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
if (error.message.toLowerCase().includes('no root type')) {
|
|
38
|
-
throw new Error(`Schema generator could not find the ${chalk_1.red(returnType)} type. Please make sure that ${chalk_1.red(returnType)} is exported.`);
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
45
|
+
cmdSchemas[commandId] = this.generateSchema(returnType, file);
|
|
46
|
+
}
|
|
47
|
+
const hookSchemas = {};
|
|
48
|
+
for (const file of this.getAllHookFiles()) {
|
|
49
|
+
const { returnType, hookId } = this.parseHookFile(file);
|
|
50
|
+
if (returnType && hookId) {
|
|
51
|
+
hookSchemas[hookId] = this.generateSchema(returnType, file);
|
|
43
52
|
}
|
|
44
53
|
}
|
|
45
|
-
return
|
|
54
|
+
return { commands: cmdSchemas, hooks: hookSchemas };
|
|
46
55
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
generateSchema(returnType, file) {
|
|
57
|
+
try {
|
|
58
|
+
const config = {
|
|
59
|
+
path: file,
|
|
60
|
+
type: returnType,
|
|
61
|
+
skipTypeCheck: true,
|
|
62
|
+
};
|
|
63
|
+
return ts_json_schema_generator_1.createGenerator(config).createSchema(config.type);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error.message.toLowerCase().includes('no root type')) {
|
|
67
|
+
throw new Error(`Schema generator could not find the ${chalk_1.red(returnType)} type. Please make sure that ${chalk_1.red(returnType)} is exported.`);
|
|
53
68
|
}
|
|
54
|
-
else
|
|
55
|
-
|
|
69
|
+
else {
|
|
70
|
+
throw error;
|
|
56
71
|
}
|
|
57
|
-
}
|
|
58
|
-
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
getAllCmdFiles() {
|
|
75
|
+
const { rootDir } = this.getDirs();
|
|
76
|
+
return getAllFiles(path.join(rootDir, 'commands'), '.ts');
|
|
77
|
+
}
|
|
78
|
+
getAllHookFiles() {
|
|
79
|
+
var _a, _b;
|
|
80
|
+
const hookFiles = Object.values((_b = (_a = this.base.config.pjson.oclif) === null || _a === void 0 ? void 0 : _a.hooks) !== null && _b !== void 0 ? _b : {}).reduce((x, y) => {
|
|
81
|
+
return Array.isArray(y) ? x.concat(y) : x.concat([y]);
|
|
82
|
+
}, []);
|
|
83
|
+
const { rootDir, outDir } = this.getDirs();
|
|
84
|
+
return hookFiles.map(h => `${path.resolve(h)}.ts`.replace(outDir, rootDir));
|
|
59
85
|
}
|
|
60
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Use regex to find the typescript type being returned by the command's
|
|
88
|
+
* `run` method.
|
|
89
|
+
* @param file the file to parse
|
|
90
|
+
* @returns Returns the name of the return type and the command id.
|
|
91
|
+
*/
|
|
92
|
+
parseCmdFile(file) {
|
|
61
93
|
const returnTypeRegex = /(?<=async\srun\(\):\sPromise<)(.*?)(>*)(?=>)/g;
|
|
62
94
|
const contents = fs.readFileSync(file, 'utf8');
|
|
63
95
|
const [returnType] = returnTypeRegex.exec(contents) || [];
|
|
@@ -71,6 +103,32 @@ class SchemaGenerator {
|
|
|
71
103
|
this.validateReturnType(returnType, commandId);
|
|
72
104
|
return { returnType, commandId };
|
|
73
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Use regex to find the typescript type being returned by the hook
|
|
108
|
+
* @param file the file to parse
|
|
109
|
+
* @returns Returns the name of the return type and the hook id.
|
|
110
|
+
*/
|
|
111
|
+
parseHookFile(file) {
|
|
112
|
+
var _a, _b;
|
|
113
|
+
const returnTypeRegex = /(?<=const\shook:\s(.*?)<)(.*?)(>*)(?=>)/g;
|
|
114
|
+
const contents = fs.readFileSync(file, 'utf8');
|
|
115
|
+
const [returnType] = returnTypeRegex.exec(contents) || [];
|
|
116
|
+
if (!returnType) {
|
|
117
|
+
return { returnType: null, hookId: null };
|
|
118
|
+
}
|
|
119
|
+
const hooks = (_b = (_a = this.base.config.pjson.oclif) === null || _a === void 0 ? void 0 : _a.hooks) !== null && _b !== void 0 ? _b : {};
|
|
120
|
+
const hookId = Object.keys(hooks).find(key => {
|
|
121
|
+
const hookFiles = (Array.isArray(hooks[key]) ? hooks[key] : [hooks[key]]);
|
|
122
|
+
const hookFileNames = hookFiles.map(f => path.basename(f).split('.')[0]);
|
|
123
|
+
const currentFileName = path.basename(file).split('.')[0];
|
|
124
|
+
return hookFileNames.includes(currentFileName);
|
|
125
|
+
});
|
|
126
|
+
if (!hookId) {
|
|
127
|
+
return { returnType: null, hookId: null };
|
|
128
|
+
}
|
|
129
|
+
this.validateReturnType(returnType, hookId);
|
|
130
|
+
return { returnType, hookId };
|
|
131
|
+
}
|
|
74
132
|
validateReturnType(returnType, commandId) {
|
|
75
133
|
const notAllowed = this.ignorevoid ? ['any', 'unknown'] : ['any', 'unknown', 'void'];
|
|
76
134
|
const vaugeTypes = ['JsonMap', 'JsonCollection', 'AnyJson'];
|
|
@@ -89,6 +147,24 @@ class SchemaGenerator {
|
|
|
89
147
|
}
|
|
90
148
|
}
|
|
91
149
|
}
|
|
150
|
+
getDirs() {
|
|
151
|
+
const dirs = {
|
|
152
|
+
rootDir: path.join(this.base.config.root, 'src'),
|
|
153
|
+
outDir: path.join(this.base.config.root, 'lib'),
|
|
154
|
+
};
|
|
155
|
+
try {
|
|
156
|
+
const tsConfig = JSON.parse(fs.readFileSync(path.join(this.base.config.root, 'tsconfig.json'), 'utf-8'));
|
|
157
|
+
if (tsConfig.compilerOptions.rootDir) {
|
|
158
|
+
dirs.rootDir = path.join(this.base.config.root, tsConfig.compilerOptions.rootDir);
|
|
159
|
+
}
|
|
160
|
+
if (tsConfig.compilerOptions.outDir) {
|
|
161
|
+
dirs.outDir = path.join(this.base.config.root, tsConfig.compilerOptions.outDir);
|
|
162
|
+
}
|
|
163
|
+
return dirs;
|
|
164
|
+
}
|
|
165
|
+
catch (_a) { }
|
|
166
|
+
return dirs;
|
|
167
|
+
}
|
|
92
168
|
}
|
|
93
169
|
exports.SchemaGenerator = SchemaGenerator;
|
|
94
170
|
class SchemaGenerate extends snapshot_command_1.SnapshotCommand {
|
|
@@ -106,13 +182,24 @@ class SchemaGenerate extends snapshot_command_1.SnapshotCommand {
|
|
|
106
182
|
files.push(filePath);
|
|
107
183
|
}
|
|
108
184
|
else {
|
|
109
|
-
for (const [cmdId, schema] of Object.entries(schemas)) {
|
|
185
|
+
for (const [cmdId, schema] of Object.entries(schemas.commands)) {
|
|
110
186
|
const fileName = `${cmdId.replace(/:/g, '-')}.json`;
|
|
111
187
|
const filePath = path.join(directory, fileName);
|
|
112
188
|
fs.writeFileSync(filePath, JSON.stringify(schema, null, 2));
|
|
113
189
|
this.log(`Generated JSON schema file "${filePath}"`);
|
|
114
190
|
files.push(filePath);
|
|
115
191
|
}
|
|
192
|
+
if (Object.values(schemas.hooks).length > 0) {
|
|
193
|
+
const hooksDir = path.join(directory, 'hooks');
|
|
194
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
195
|
+
for (const [hookId, schema] of Object.entries(schemas.hooks)) {
|
|
196
|
+
const fileName = `${hookId.replace(/:/g, '-')}.json`;
|
|
197
|
+
const filePath = path.join(hooksDir, fileName);
|
|
198
|
+
fs.writeFileSync(filePath, JSON.stringify(schema, null, 2));
|
|
199
|
+
this.log(`Generated JSON schema file "${filePath}"`);
|
|
200
|
+
files.push(filePath);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
116
203
|
}
|
|
117
204
|
return files;
|
|
118
205
|
}
|
package/oclif.manifest.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"3.
|
|
1
|
+
{"version":"3.1.0","commands":{"schema:compare":{"id":"schema:compare","strict":true,"pluginName":"@oclif/plugin-command-snapshot","pluginAlias":"@oclif/plugin-command-snapshot","pluginType":"core","aliases":[],"flags":{"filepath":{"name":"filepath","type":"option","description":"path of the generated snapshot file","multiple":false,"default":"./schemas"}},"args":[]},"schema:generate":{"id":"schema:generate","strict":true,"pluginName":"@oclif/plugin-command-snapshot","pluginAlias":"@oclif/plugin-command-snapshot","pluginType":"core","aliases":[],"flags":{"filepath":{"name":"filepath","type":"option","description":"directory to save the generated schema files; can use \"{version}\" to insert the current CLI/plugin version","multiple":false,"default":"./schemas"},"singlefile":{"name":"singlefile","type":"boolean","description":"put generated schema into a single file","allowNo":false},"ignorevoid":{"name":"ignorevoid","type":"boolean","description":"ignore commands that return void","allowNo":false}},"args":[]},"snapshot:compare":{"id":"snapshot:compare","strict":true,"pluginName":"@oclif/plugin-command-snapshot","pluginAlias":"@oclif/plugin-command-snapshot","pluginType":"core","aliases":[],"flags":{"filepath":{"name":"filepath","type":"option","description":"path of the generated snapshot file","multiple":false,"default":"./command-snapshot.json"}},"args":[]},"snapshot:generate":{"id":"snapshot:generate","strict":true,"pluginName":"@oclif/plugin-command-snapshot","pluginAlias":"@oclif/plugin-command-snapshot","pluginType":"core","aliases":[],"flags":{"filepath":{"name":"filepath","type":"option","description":"path to save the generated snapshot file; can use \"{version}\" to replace the current CLI/plugin version","multiple":false,"default":"./command-snapshot.json"}},"args":[]}}}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oclif/plugin-command-snapshot",
|
|
3
3
|
"description": "generates and compares OCLIF plugins snapshot files",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.1.0",
|
|
5
5
|
"author": "Ramyasri @nramyasri-sf",
|
|
6
6
|
"bugs": "https://github.com/oclif/plugin-command-snapshot/issues",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@oclif/core": "^0.
|
|
8
|
+
"@oclif/core": "^1.0.1",
|
|
9
9
|
"chalk": "^4.1.1",
|
|
10
10
|
"just-diff": "^3.1.1",
|
|
11
11
|
"semver": "^7.3.5",
|
package/yarn.lock
CHANGED
|
@@ -460,6 +460,29 @@
|
|
|
460
460
|
widest-line "^3.1.0"
|
|
461
461
|
wrap-ansi "^7.0.0"
|
|
462
462
|
|
|
463
|
+
"@oclif/core@^1.0.1":
|
|
464
|
+
version "1.0.1"
|
|
465
|
+
resolved "https://registry.npmjs.org/@oclif/core/-/core-1.0.1.tgz#e97fc5c234db1a745fb4468eb79cabeea5f84af9"
|
|
466
|
+
integrity sha512-ai9XwnEZ+P34VUtV+fX/6HmRbo5CXGdZjX3iK3PsY6O+KYeJBKVAkPCKLz0qtAHxx0HX4+Yj2wnmy+hTCx6HYQ==
|
|
467
|
+
dependencies:
|
|
468
|
+
"@oclif/linewrap" "^1.0.0"
|
|
469
|
+
chalk "^4.1.0"
|
|
470
|
+
clean-stack "^3.0.0"
|
|
471
|
+
cli-ux "^5.1.0"
|
|
472
|
+
debug "^4.1.1"
|
|
473
|
+
fs-extra "^9.0.1"
|
|
474
|
+
get-package-type "^0.1.0"
|
|
475
|
+
globby "^11.0.1"
|
|
476
|
+
indent-string "^4.0.0"
|
|
477
|
+
is-wsl "^2.1.1"
|
|
478
|
+
lodash.template "^4.4.0"
|
|
479
|
+
semver "^7.3.2"
|
|
480
|
+
string-width "^4.2.0"
|
|
481
|
+
strip-ansi "^6.0.0"
|
|
482
|
+
tslib "^2.0.0"
|
|
483
|
+
widest-line "^3.1.0"
|
|
484
|
+
wrap-ansi "^7.0.0"
|
|
485
|
+
|
|
463
486
|
"@oclif/errors@^1.2.1", "@oclif/errors@^1.2.2", "@oclif/errors@^1.3.3":
|
|
464
487
|
version "1.3.4"
|
|
465
488
|
resolved "https://registry.npmjs.org/@oclif/errors/-/errors-1.3.4.tgz#a96f94536b4e25caa72eff47e8b3ed04f6995f55"
|