@oclif/plugin-command-snapshot
Advanced tools
+13
-0
@@ -5,2 +5,15 @@ # Changelog | ||
| ## [3.1.0](https://github.com/oclif/plugin-command-snapshot/compare/v3.0.0...v3.1.0) (2021-10-08) | ||
| ### Features | ||
| * generate schema for hooks ([266b832](https://github.com/oclif/plugin-command-snapshot/commit/266b83242fab1961b036998119791b999a08ee19)) | ||
| ### Bug Fixes | ||
| * ignore hooks with no return type ([070ed41](https://github.com/oclif/plugin-command-snapshot/commit/070ed41902268d48034fdaa2b96f9a4a54eefd2c)) | ||
| * schema:compare with hooks ([508549a](https://github.com/oclif/plugin-command-snapshot/commit/508549a372e56b3d79dec970e62477d9905dec13)) | ||
| ### [2.2.2](https://github.com/oclif/plugin-command-snapshot/compare/v2.2.1...v2.2.2) (2021-06-30) | ||
@@ -7,0 +20,0 @@ |
@@ -23,3 +23,5 @@ "use strict"; | ||
| const existingSchema = this.readExistingSchema(flags.filepath); | ||
| const latestSchema = await this.generateLatestSchema(); | ||
| const latestSchema = (await this.generateLatestSchema()); | ||
| this.debug('existingSchema', existingSchema); | ||
| this.debug('latestSchema', latestSchema); | ||
| const changes = just_diff_1.diff(latestSchema, existingSchema); | ||
@@ -48,3 +50,3 @@ if (changes.length === 0) { | ||
| case 'remove': | ||
| humandReadableChanges[commandId].push(`${chalk_1.underline(readablePath)} was ${chalk_1.cyan('removed')} from latest schema`); | ||
| humandReadableChanges[commandId].push(`${chalk_1.underline(readablePath)} was ${chalk_1.cyan('not found')} in latest schema`); | ||
| break; | ||
@@ -73,4 +75,7 @@ default: | ||
| const schemasDir = folderIsVersioned ? path.join(filePath, semver.rsort(contents)[0] || '') : filePath; | ||
| const schemaFiles = fs.readdirSync(schemasDir).map(f => path.join(schemasDir, f)).filter(f => !fs.statSync(f).isDirectory()); | ||
| let schemas = {}; | ||
| const schemaFiles = generate_1.getAllFiles(schemasDir, '.json'); | ||
| let schemas = { | ||
| commands: {}, | ||
| hooks: {}, | ||
| }; | ||
| if (schemaFiles.length === 1 && schemaFiles[0].endsWith('schema.json')) { | ||
@@ -82,3 +87,9 @@ schemas = JSON.parse(fs.readFileSync(schemaFiles[0]).toString('utf8')); | ||
| const schema = JSON.parse(fs.readFileSync(file).toString('utf8')); | ||
| schemas[path.basename(file.replace(/-/g, ':')).replace('.json', '')] = schema; | ||
| const key = path.basename(file.replace(/-/g, ':')).replace('.json', ''); | ||
| if (file.split(path.sep).includes('hooks')) { | ||
| schemas.hooks[key] = schema; | ||
| } | ||
| else { | ||
| schemas.commands[key] = schema; | ||
| } | ||
| } | ||
@@ -85,0 +96,0 @@ } |
@@ -6,3 +6,8 @@ import { Schema } from 'ts-json-schema-generator'; | ||
| }; | ||
| export declare type Schemas = { | ||
| commands: SchemasMap; | ||
| hooks: SchemasMap; | ||
| }; | ||
| export declare type GenerateResponse = string[]; | ||
| export declare function getAllFiles(dirPath: string, ext: string, allFiles?: string[]): string[]; | ||
| export declare class SchemaGenerator { | ||
@@ -13,7 +18,22 @@ private base; | ||
| constructor(base: SnapshotCommand, ignorevoid?: boolean); | ||
| generate(): Promise<SchemasMap>; | ||
| generate(): Promise<Schemas>; | ||
| private generateSchema; | ||
| private getAllCmdFiles; | ||
| private parseFile; | ||
| private getAllHookFiles; | ||
| /** | ||
| * Use regex to find the typescript type being returned by the command's | ||
| * `run` method. | ||
| * @param file the file to parse | ||
| * @returns Returns the name of the return type and the command id. | ||
| */ | ||
| private parseCmdFile; | ||
| /** | ||
| * Use regex to find the typescript type being returned by the hook | ||
| * @param file the file to parse | ||
| * @returns Returns the name of the return type and the hook id. | ||
| */ | ||
| private parseHookFile; | ||
| private validateReturnType; | ||
| private determineCommandId; | ||
| private getDirs; | ||
| } | ||
@@ -20,0 +40,0 @@ export default class SchemaGenerate extends SnapshotCommand { |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.SchemaGenerator = void 0; | ||
| exports.SchemaGenerator = exports.getAllFiles = void 0; | ||
| const path = require("path"); | ||
@@ -10,2 +10,20 @@ const fs = require("fs"); | ||
| const chalk_1 = require("chalk"); | ||
| function getAllFiles(dirPath, ext, allFiles = []) { | ||
| let files = []; | ||
| try { | ||
| files = fs.readdirSync(dirPath); | ||
| } | ||
| catch (_a) { } | ||
| files.forEach(file => { | ||
| const fPath = path.join(dirPath, file); | ||
| if (fs.statSync(fPath).isDirectory()) { | ||
| allFiles = getAllFiles(fPath, ext, allFiles); | ||
| } | ||
| else if (file.endsWith(ext)) { | ||
| allFiles.push(fPath); | ||
| } | ||
| }); | ||
| return allFiles; | ||
| } | ||
| exports.getAllFiles = getAllFiles; | ||
| class SchemaGenerator { | ||
@@ -23,41 +41,55 @@ constructor(base, ignorevoid = true) { | ||
| } | ||
| const schemas = {}; | ||
| const cmdSchemas = {}; | ||
| for (const file of this.getAllCmdFiles()) { | ||
| const { returnType, commandId } = this.parseFile(file); | ||
| const { returnType, commandId } = this.parseCmdFile(file); | ||
| if (this.ignorevoid && returnType === 'void') | ||
| continue; | ||
| try { | ||
| const config = { | ||
| path: file, | ||
| type: returnType, | ||
| skipTypeCheck: true, | ||
| }; | ||
| const schema = ts_json_schema_generator_1.createGenerator(config).createSchema(config.type); | ||
| schemas[commandId] = schema; | ||
| cmdSchemas[commandId] = this.generateSchema(returnType, file); | ||
| } | ||
| const hookSchemas = {}; | ||
| for (const file of this.getAllHookFiles()) { | ||
| const { returnType, hookId } = this.parseHookFile(file); | ||
| if (returnType && hookId) { | ||
| hookSchemas[hookId] = this.generateSchema(returnType, file); | ||
| } | ||
| catch (error) { | ||
| if (error.message.toLowerCase().includes('no root type')) { | ||
| throw new Error(`Schema generator could not find the ${chalk_1.red(returnType)} type. Please make sure that ${chalk_1.red(returnType)} is exported.`); | ||
| } | ||
| else { | ||
| throw error; | ||
| } | ||
| } | ||
| } | ||
| return schemas; | ||
| return { commands: cmdSchemas, hooks: hookSchemas }; | ||
| } | ||
| getAllCmdFiles(dirPath = path.join(this.base.config.root, 'src', 'commands'), allFiles = []) { | ||
| const files = fs.readdirSync(dirPath); | ||
| files.forEach(file => { | ||
| const fPath = path.join(dirPath, file); | ||
| if (fs.statSync(fPath).isDirectory()) { | ||
| allFiles = this.getAllCmdFiles(fPath, allFiles); | ||
| generateSchema(returnType, file) { | ||
| try { | ||
| const config = { | ||
| path: file, | ||
| type: returnType, | ||
| skipTypeCheck: true, | ||
| }; | ||
| return ts_json_schema_generator_1.createGenerator(config).createSchema(config.type); | ||
| } | ||
| catch (error) { | ||
| if (error.message.toLowerCase().includes('no root type')) { | ||
| throw new Error(`Schema generator could not find the ${chalk_1.red(returnType)} type. Please make sure that ${chalk_1.red(returnType)} is exported.`); | ||
| } | ||
| else if (file.endsWith('ts')) { | ||
| allFiles.push(fPath); | ||
| else { | ||
| throw error; | ||
| } | ||
| }); | ||
| return allFiles; | ||
| } | ||
| } | ||
| parseFile(file) { | ||
| getAllCmdFiles() { | ||
| const { rootDir } = this.getDirs(); | ||
| return getAllFiles(path.join(rootDir, 'commands'), '.ts'); | ||
| } | ||
| getAllHookFiles() { | ||
| var _a, _b; | ||
| 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) => { | ||
| return Array.isArray(y) ? x.concat(y) : x.concat([y]); | ||
| }, []); | ||
| const { rootDir, outDir } = this.getDirs(); | ||
| return hookFiles.map(h => `${path.resolve(h)}.ts`.replace(outDir, rootDir)); | ||
| } | ||
| /** | ||
| * Use regex to find the typescript type being returned by the command's | ||
| * `run` method. | ||
| * @param file the file to parse | ||
| * @returns Returns the name of the return type and the command id. | ||
| */ | ||
| parseCmdFile(file) { | ||
| const returnTypeRegex = /(?<=async\srun\(\):\sPromise<)(.*?)(>*)(?=>)/g; | ||
@@ -76,2 +108,28 @@ const contents = fs.readFileSync(file, 'utf8'); | ||
| } | ||
| /** | ||
| * Use regex to find the typescript type being returned by the hook | ||
| * @param file the file to parse | ||
| * @returns Returns the name of the return type and the hook id. | ||
| */ | ||
| parseHookFile(file) { | ||
| var _a, _b; | ||
| const returnTypeRegex = /(?<=const\shook:\s(.*?)<)(.*?)(>*)(?=>)/g; | ||
| const contents = fs.readFileSync(file, 'utf8'); | ||
| const [returnType] = returnTypeRegex.exec(contents) || []; | ||
| if (!returnType) { | ||
| return { returnType: null, hookId: null }; | ||
| } | ||
| const hooks = (_b = (_a = this.base.config.pjson.oclif) === null || _a === void 0 ? void 0 : _a.hooks) !== null && _b !== void 0 ? _b : {}; | ||
| const hookId = Object.keys(hooks).find(key => { | ||
| const hookFiles = (Array.isArray(hooks[key]) ? hooks[key] : [hooks[key]]); | ||
| const hookFileNames = hookFiles.map(f => path.basename(f).split('.')[0]); | ||
| const currentFileName = path.basename(file).split('.')[0]; | ||
| return hookFileNames.includes(currentFileName); | ||
| }); | ||
| if (!hookId) { | ||
| return { returnType: null, hookId: null }; | ||
| } | ||
| this.validateReturnType(returnType, hookId); | ||
| return { returnType, hookId }; | ||
| } | ||
| validateReturnType(returnType, commandId) { | ||
@@ -95,2 +153,20 @@ const notAllowed = this.ignorevoid ? ['any', 'unknown'] : ['any', 'unknown', 'void']; | ||
| } | ||
| getDirs() { | ||
| const dirs = { | ||
| rootDir: path.join(this.base.config.root, 'src'), | ||
| outDir: path.join(this.base.config.root, 'lib'), | ||
| }; | ||
| try { | ||
| const tsConfig = JSON.parse(fs.readFileSync(path.join(this.base.config.root, 'tsconfig.json'), 'utf-8')); | ||
| if (tsConfig.compilerOptions.rootDir) { | ||
| dirs.rootDir = path.join(this.base.config.root, tsConfig.compilerOptions.rootDir); | ||
| } | ||
| if (tsConfig.compilerOptions.outDir) { | ||
| dirs.outDir = path.join(this.base.config.root, tsConfig.compilerOptions.outDir); | ||
| } | ||
| return dirs; | ||
| } | ||
| catch (_a) { } | ||
| return dirs; | ||
| } | ||
| } | ||
@@ -113,3 +189,3 @@ exports.SchemaGenerator = SchemaGenerator; | ||
| else { | ||
| for (const [cmdId, schema] of Object.entries(schemas)) { | ||
| for (const [cmdId, schema] of Object.entries(schemas.commands)) { | ||
| const fileName = `${cmdId.replace(/:/g, '-')}.json`; | ||
@@ -121,2 +197,13 @@ const filePath = path.join(directory, fileName); | ||
| } | ||
| if (Object.values(schemas.hooks).length > 0) { | ||
| const hooksDir = path.join(directory, 'hooks'); | ||
| fs.mkdirSync(hooksDir, { recursive: true }); | ||
| for (const [hookId, schema] of Object.entries(schemas.hooks)) { | ||
| const fileName = `${hookId.replace(/:/g, '-')}.json`; | ||
| const filePath = path.join(hooksDir, fileName); | ||
| fs.writeFileSync(filePath, JSON.stringify(schema, null, 2)); | ||
| this.log(`Generated JSON schema file "${filePath}"`); | ||
| files.push(filePath); | ||
| } | ||
| } | ||
| } | ||
@@ -123,0 +210,0 @@ return files; |
@@ -1,1 +0,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":[]}}} | ||
| {"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":[]}}} |
+2
-2
| { | ||
| "name": "@oclif/plugin-command-snapshot", | ||
| "description": "generates and compares OCLIF plugins snapshot files", | ||
| "version": "3.0.0", | ||
| "version": "3.1.0", | ||
| "author": "Ramyasri @nramyasri-sf", | ||
| "bugs": "https://github.com/oclif/plugin-command-snapshot/issues", | ||
| "dependencies": { | ||
| "@oclif/core": "^0.5.39", | ||
| "@oclif/core": "^1.0.1", | ||
| "chalk": "^4.1.1", | ||
@@ -10,0 +10,0 @@ "just-diff": "^3.1.1", |
Sorry, the diff of this file is too big to display
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
303385
2.02%784
17.72%0
-100%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated