Skip to content

Commit

Permalink
feat: support pattern strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
mdonnalley committed Feb 15, 2024
1 parent 19a332c commit f3843fd
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 34 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@oclif/core": "^3.18.2",
"@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

Check failure on line 36 in src/commands/schema/compare.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/*)

Property 'strategy' does not exist on type 'never'.

Check failure on line 36 in src/commands/schema/compare.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/-1)

Property 'strategy' does not exist on type 'never'.
const commandsDir =
typeof this.config.pjson.oclif?.commands === 'string'
? this.config.pjson.oclif?.commands
: this.config.pjson.oclif?.commands?.target

Check failure on line 40 in src/commands/schema/compare.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/*)

Property 'target' does not exist on type 'never'.

Check failure on line 40 in src/commands/schema/compare.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/-1)

Property 'target' does not exist on type 'never'.
const commandGlobs =
typeof this.config.pjson.oclif?.commands === 'string'
? GLOB_PATTERNS
: this.config.pjson.oclif?.commands?.globPatterns

Check failure on line 44 in src/commands/schema/compare.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/*)

Property 'globPatterns' does not exist on type 'never'.

Check failure on line 44 in src/commands/schema/compare.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/-1)

Property 'globPatterns' does not exist on type 'never'.

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

Check failure on line 221 in src/commands/schema/generate.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/*)

Property 'strategy' does not exist on type 'never'.

Check failure on line 221 in src/commands/schema/generate.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/-1)

Property 'strategy' does not exist on type 'never'.
const commandsDir =
typeof this.config.pjson.oclif?.commands === 'string'
? this.config.pjson.oclif?.commands
: this.config.pjson.oclif?.commands?.target

Check failure on line 225 in src/commands/schema/generate.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/*)

Property 'target' does not exist on type 'never'.

Check failure on line 225 in src/commands/schema/generate.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/-1)

Property 'target' does not exist on type 'never'.
const commandGlobs =
typeof this.config.pjson.oclif?.commands === 'string'
? GLOB_PATTERNS
: this.config.pjson.oclif?.commands?.globPatterns

Check failure on line 229 in src/commands/schema/generate.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/*)

Property 'globPatterns' does not exist on type 'never'.

Check failure on line 229 in src/commands/schema/generate.ts

View workflow job for this annotation

GitHub Actions / linux-unit-tests / linux-unit-tests (lts/-1)

Property 'globPatterns' does not exist on type 'never'.

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
26 changes: 26 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,26 @@ export const getSchemaFileName = (cmdId: string): string => {
*/
export const getKeyNameFromFilename = (file: string): string =>
file.replaceAll('-', ':').replaceAll('__', '-').replace('.json', '')

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
}

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)',
]
43 changes: 43 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,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 @@ -4011,6 +4016,17 @@ fast-glob@^3.3.1:
merge2 "^1.3.0"
micromatch "^4.0.4"

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==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
glob-parent "^5.1.2"
merge2 "^1.3.0"
micromatch "^4.0.4"

fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
Expand Down Expand Up @@ -4415,6 +4431,18 @@ globby@^11.0.1, globby@^11.0.3, globby@^11.1.0:
merge2 "^1.4.1"
slash "^3.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 @@ -6432,6 +6460,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 @@ -7038,6 +7071,11 @@ slash@^3.0.0:
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==

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 @@ -7605,6 +7643,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 f3843fd

Please sign in to comment.