@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 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('removed')} from latest schema`);
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 = fs.readdirSync(schemasDir).map(f => path.join(schemasDir, f)).filter(f => !fs.statSync(f).isDirectory());
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
- schemas[path.basename(file.replace(/-/g, ':')).replace('.json', '')] = schema;
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<SchemasMap>;
17
+ generate(): Promise<Schemas>;
18
+ private generateSchema;
13
19
  private getAllCmdFiles;
14
- private parseFile;
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 schemas = {};
40
+ const cmdSchemas = {};
23
41
  for (const file of this.getAllCmdFiles()) {
24
- const { returnType, commandId } = this.parseFile(file);
42
+ const { returnType, commandId } = this.parseCmdFile(file);
25
43
  if (this.ignorevoid && returnType === 'void')
26
44
  continue;
27
- try {
28
- const config = {
29
- path: file,
30
- type: returnType,
31
- skipTypeCheck: true,
32
- };
33
- const schema = ts_json_schema_generator_1.createGenerator(config).createSchema(config.type);
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 schemas;
54
+ return { commands: cmdSchemas, hooks: hookSchemas };
46
55
  }
47
- getAllCmdFiles(dirPath = path.join(this.base.config.root, 'src', 'commands'), allFiles = []) {
48
- const files = fs.readdirSync(dirPath);
49
- files.forEach(file => {
50
- const fPath = path.join(dirPath, file);
51
- if (fs.statSync(fPath).isDirectory()) {
52
- allFiles = this.getAllCmdFiles(fPath, allFiles);
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 if (file.endsWith('ts')) {
55
- allFiles.push(fPath);
69
+ else {
70
+ throw error;
56
71
  }
57
- });
58
- return allFiles;
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
- parseFile(file) {
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
  }
@@ -1 +1 @@
1
- {"version":"3.0.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":[]}}}
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.0.0",
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.5.39",
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"