Skip to content

Commit

Permalink
feat: support pattern strategy (#482)
Browse files Browse the repository at this point in the history
* feat: support pattern strategy

* chore: use dev tag for oclif/core

* chore: code review

* chore: bump core
  • Loading branch information
mdonnalley authored Mar 4, 2024
1 parent 0670c15 commit 687e764
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 37 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
"author": "Salesforce",
"bugs": "https://github.com/oclif/plugin-command-snapshot/issues",
"dependencies": {
"@oclif/core": "^3.20.0",
"@oclif/core": "3.21.0",
"@types/lodash.difference": "^4.5.9",
"chalk": "^5.3.0",
"globby": "^14.0.1",
"just-diff": "^5.2.0",
"lodash.difference": "^4.5.0",
"lodash.get": "^4.4.2",
Expand Down
33 changes: 24 additions & 9 deletions src/commands/schema/compare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import * as semver from 'semver'
import {Schema} from 'ts-json-schema-generator'

import SnapshotCommand from '../../snapshot-command.js'
import {getKeyNameFromFilename} from '../../util.js'
import {SchemaGenerator, Schemas, getAllFiles} from './generate.js'
import {GLOB_PATTERNS, getAllFiles, getKeyNameFromFilename} from '../../util.js'
import {SchemaGenerator, Schemas} from './generate.js'

export type SchemaComparison = Array<{op: Operation; path: (number | string)[]; value: unknown}>

Expand All @@ -32,6 +32,25 @@ export default class SchemaCompare extends SnapshotCommand {
}

public async run(): Promise<SchemaComparison> {
const strategy =
typeof this.config.pjson.oclif?.commands === 'string' ? 'pattern' : this.config.pjson.oclif?.commands?.strategy
const commandsDir =
typeof this.config.pjson.oclif?.commands === 'string'
? this.config.pjson.oclif?.commands
: this.config.pjson.oclif?.commands?.target
const commandGlobs =
typeof this.config.pjson.oclif?.commands === 'string'
? GLOB_PATTERNS
: this.config.pjson.oclif?.commands?.globPatterns

if (strategy === 'single') {
this.error('This command is not supported for single command CLIs')
}

if (strategy === 'explicit') {
this.error('This command is not supported for explicit command discovery')
}

const {flags} = await this.parse(SchemaCompare)

try {
Expand All @@ -42,7 +61,8 @@ export default class SchemaCompare extends SnapshotCommand {
}

const existingSchema = this.readExistingSchema(flags.filepath)
const latestSchema = await this.generateLatestSchema()
const generator = new SchemaGenerator({base: this, commandGlobs, commandsDir})
const latestSchema = await generator.generate()
this.debug('existingSchema', existingSchema)
this.debug('latestSchema', latestSchema)
const changes = diff(latestSchema, existingSchema)
Expand Down Expand Up @@ -113,7 +133,7 @@ export default class SchemaCompare extends SnapshotCommand {
}

this.log()
const bin = process.platform === 'win32' ? 'bin\\dev.cmd' : 'bin/dev'
const bin = process.platform === 'win32' ? 'bin\\dev.cmd' : 'bin/dev.js'
this.log(
'If intended, please update the schema file(s) and run again:',
chalk.bold(`${bin} ${toConfiguredId('schema:generate', this.config)}`),
Expand All @@ -122,11 +142,6 @@ export default class SchemaCompare extends SnapshotCommand {
return changes
}

private async generateLatestSchema(): Promise<Schemas> {
const generator = new SchemaGenerator(this)
return generator.generate()
}

private readExistingSchema(filePath: string): Schemas {
const contents = fs.readdirSync(filePath)
const folderIsVersioned = contents.every((c) => semver.valid(c))
Expand Down
69 changes: 44 additions & 25 deletions src/commands/schema/generate.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {Flags} from '@oclif/core'
import chalk from 'chalk'
import {globbySync} from 'globby'
import * as fs from 'node:fs'
import * as path from 'node:path'
import {Schema, createGenerator} from 'ts-json-schema-generator'

import SnapshotCommand from '../../snapshot-command.js'
import {getSchemaFileName} from '../../util.js'
import {GLOB_PATTERNS, getAllFiles, getSchemaFileName} from '../../util.js'

export type SchemasMap = {
[key: string]: Schema
Expand All @@ -15,32 +16,26 @@ export type Schemas = {commands: SchemasMap; hooks: SchemasMap}

export type GenerateResponse = string[]

export function getAllFiles(dirPath: string, ext: string, allFiles: string[] = []): string[] {
let files: string[] = []
try {
files = fs.readdirSync(dirPath)
} catch {}

for (const file of files) {
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
type SchemaGenerateOptions = {
base: SnapshotCommand
commandGlobs?: string[]
commandsDir?: string
ignoreVoid?: boolean
}

export class SchemaGenerator {
private base: SnapshotCommand
private classToId: Record<string, string> = {}

// eslint-disable-next-line no-useless-constructor
constructor(
private base: SnapshotCommand,
private ignoreVoid = true,
) {}
private commandGlobs: string[]
private commandsDir: string | undefined
private ignoreVoid: boolean

constructor(options: SchemaGenerateOptions) {
this.base = options.base
this.ignoreVoid = options.ignoreVoid ?? true
this.commandsDir = options.commandsDir
this.commandGlobs = options.commandGlobs ?? GLOB_PATTERNS
}

public async generate(): Promise<Schemas> {
for (const cmd of this.base.commands) {
Expand Down Expand Up @@ -102,7 +97,12 @@ export class SchemaGenerator {
}

private getAllCmdFiles(): string[] {
const {rootDir} = this.getDirs()
const {outDir, rootDir} = this.getDirs()
if (this.commandsDir) {
const commandsSrcDir = path.resolve(this.base.config.root, this.commandsDir).replace(outDir, rootDir)
return globbySync(this.commandGlobs, {absolute: true, cwd: commandsSrcDir})
}

return getAllFiles(path.join(rootDir, 'commands'), '.ts')
}

Expand Down Expand Up @@ -217,8 +217,27 @@ export default class SchemaGenerate extends SnapshotCommand {
}

public async run(): Promise<GenerateResponse> {
const strategy =
typeof this.config.pjson.oclif?.commands === 'string' ? 'pattern' : this.config.pjson.oclif?.commands?.strategy
const commandsDir =
typeof this.config.pjson.oclif?.commands === 'string'
? this.config.pjson.oclif?.commands
: this.config.pjson.oclif?.commands?.target
const commandGlobs =
typeof this.config.pjson.oclif?.commands === 'string'
? GLOB_PATTERNS
: this.config.pjson.oclif?.commands?.globPatterns

if (strategy === 'single') {
this.error('This command is not supported for single command CLIs')
}

if (strategy === 'explicit') {
this.error('This command is not supported for explicit command discovery')
}

const {flags} = await this.parse(SchemaGenerate)
const generator = new SchemaGenerator(this, flags.ignorevoid)
const generator = new SchemaGenerator({base: this, commandGlobs, commandsDir, ignoreVoid: flags.ignorevoid})

const schemas = await generator.generate()

Expand Down
24 changes: 24 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import fs from 'node:fs'
import path from 'node:path'

/**
* Get the file name for a given command ID replacing "-" with "__" and ":" with "-"
* @param cmdId - command ID
Expand All @@ -15,3 +18,24 @@ export const getSchemaFileName = (cmdId: string): string => {
*/
export const getKeyNameFromFilename = (file: string): string =>
file.replaceAll('-', ':').replaceAll('__', '-').replace('.json', '')

export const getAllFiles = (dirPath: string, ext: string, allFiles: string[] = []): string[] =>
safeReadDirSync(dirPath)
.flatMap((f) =>
f.isDirectory() ? getAllFiles(path.join(dirPath, f.name), ext, allFiles) : path.join(dirPath, f.name),
)
.filter((f) => f.endsWith(ext))

const safeReadDirSync = (dirPath: string): fs.Dirent[] => {
try {
// TODO: use recursive option when available in Node 20
return fs.readdirSync(dirPath, {withFileTypes: true})
} catch {
return []
}
}

export const GLOB_PATTERNS = [
'**/*.+(js|cjs|mjs|ts|tsx|mts|cts)',
'!**/*.+(d.ts|test.ts|test.js|spec.ts|spec.js|d.mts|d.cts)?(x)',
]
69 changes: 67 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1311,6 +1311,39 @@
node-gyp "^8.2.0"
read-package-json-fast "^2.0.1"

"@oclif/[email protected]":
version "3.21.0"
resolved "https://registry.yarnpkg.com/@oclif/core/-/core-3.21.0.tgz#a235704e66589c8c104ccd616d0a8f1f36cf693e"
integrity sha512-xR7qGPOWtOnYmdYocSn6oEh2oTQLsPOXoj3HYGpb26V3WulwF8Cm33WPnMsSISv4ben3Rtc5i59u9O5NnuG42g==
dependencies:
"@types/cli-progress" "^3.11.5"
ansi-escapes "^4.3.2"
ansi-styles "^4.3.0"
cardinal "^2.1.1"
chalk "^4.1.2"
clean-stack "^3.0.1"
cli-progress "^3.12.0"
color "^4.2.3"
debug "^4.3.4"
ejs "^3.1.9"
get-package-type "^0.1.0"
globby "^11.1.0"
hyperlinker "^1.0.0"
indent-string "^4.0.0"
is-wsl "^2.2.0"
js-yaml "^3.14.1"
natural-orderby "^2.0.3"
object-treeify "^1.1.33"
password-prompt "^1.1.3"
slice-ansi "^4.0.0"
string-width "^4.2.3"
strip-ansi "^6.0.1"
supports-color "^8.1.1"
supports-hyperlinks "^2.2.0"
widest-line "^3.1.0"
wordwrap "^1.0.0"
wrap-ansi "^7.0.0"

"@oclif/core@^2.15.0":
version "2.15.0"
resolved "https://registry.yarnpkg.com/@oclif/core/-/core-2.15.0.tgz#f27797b30a77d13279fba88c1698fc34a0bd0d2a"
Expand Down Expand Up @@ -1345,7 +1378,7 @@
wordwrap "^1.0.0"
wrap-ansi "^7.0.0"

"@oclif/core@^3.18.1", "@oclif/core@^3.19.2", "@oclif/core@^3.19.3", "@oclif/core@^3.20.0":
"@oclif/core@^3.18.1", "@oclif/core@^3.19.2", "@oclif/core@^3.19.3":
version "3.20.0"
resolved "https://registry.yarnpkg.com/@oclif/core/-/core-3.20.0.tgz#534458dc6e8c46d8f03906aaadaca079e16a6554"
integrity sha512-8BajhglY8frYGAS1whAukeouFZUN9MgQoLfNXtScPVEAjPlaD2BbSIAYQH2yF2qb/iVvbj/1DwYS3gqicYOq1A==
Expand Down Expand Up @@ -1532,6 +1565,11 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==

"@sindresorhus/merge-streams@^2.1.0":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.2.1.tgz#82b5e1e135ef62ef8b522d6e7f43ad360a69f294"
integrity sha512-255V7MMIKw6aQ43Wbqp9HZ+VHn6acddERTLiiLnlcPLU9PdTq9Aijl12oklAgUEblLWye+vHLzmqBx6f2TGcZw==

"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
Expand Down Expand Up @@ -4105,7 +4143,7 @@ fast-glob@^3.2.9:
merge2 "^1.3.0"
micromatch "^4.0.4"

fast-glob@^3.3.0:
fast-glob@^3.3.0, fast-glob@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
Expand Down Expand Up @@ -4552,6 +4590,18 @@ globby@^13.1.2:
merge2 "^1.4.1"
slash "^4.0.0"

globby@^14.0.1:
version "14.0.1"
resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.1.tgz#a1b44841aa7f4c6d8af2bc39951109d77301959b"
integrity sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==
dependencies:
"@sindresorhus/merge-streams" "^2.1.0"
fast-glob "^3.3.2"
ignore "^5.2.4"
path-type "^5.0.0"
slash "^5.1.0"
unicorn-magic "^0.1.0"

gopd@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
Expand Down Expand Up @@ -6575,6 +6625,11 @@ path-type@^4.0.0:
resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==

path-type@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8"
integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==

pathval@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
Expand Down Expand Up @@ -7179,6 +7234,11 @@ slash@^4.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==

slash@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce"
integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==

slice-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
Expand Down Expand Up @@ -7764,6 +7824,11 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==

unicorn-magic@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4"
integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==

unique-filename@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
Expand Down

0 comments on commit 687e764

Please sign in to comment.