From da1b265aceca0c09ff22a03ef349d0c273bc213d Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Thu, 25 Jul 2024 14:50:39 -0600 Subject: [PATCH 1/6] feat: generate command without yeoman --- command-snapshot.json | 2 +- messages/dev.generate.command.md | 10 +- package.json | 1 + src/commands/dev/generate/command.ts | 141 ++++++++++++++++++- src/generator.ts | 109 +++++++++++++++ src/generators/command.ts | 193 --------------------------- 6 files changed, 251 insertions(+), 205 deletions(-) create mode 100644 src/generator.ts delete mode 100644 src/generators/command.ts diff --git a/command-snapshot.json b/command-snapshot.json index 3fd7ab3d..ea8eba2c 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -44,7 +44,7 @@ "command": "dev:generate:command", "flagAliases": [], "flagChars": ["n"], - "flags": ["flags-dir", "force", "name", "nuts", "unit"], + "flags": ["dry-run", "flags-dir", "force", "name", "nuts", "unit"], "plugin": "@salesforce/plugin-dev" }, { diff --git a/messages/dev.generate.command.md b/messages/dev.generate.command.md index fcbde9d0..1c2004bf 100644 --- a/messages/dev.generate.command.md +++ b/messages/dev.generate.command.md @@ -6,7 +6,7 @@ Generate a new sf command. You must run this command from within a plugin directory, such as the directory created with the "sf dev generate plugin" command. -The command generates basic source files, messages (\*.md), and test files for your new command. The Typescript files contain import statements for the minimum required Salesforce libraries, and scaffold some basic code. The new type names come from the value you passed to the --name flag. +The command generates basic source files, messages (\*.md), and test files for your new command. The Typescript files contain import statements for the minimum required Salesforce libraries, and scaffold some basic code. The new type names come from the value you passed to the --name flag. The command updates the package.json file, so if it detects conflicts with the existing file, you're prompted whether you want to overwrite the file. There are a number of package.json updates required for a new command, so we recommend you answer "y" so the command takes care of them all. If you answer "n", you must update the package.json file manually. @@ -26,12 +26,12 @@ Generate a unit test file for the command. Name of the new command. Use colons to separate the topic and command names. +# flags.dry-run.summary + +Output the generated files without writing them to disk. + # examples - Generate the files for a new "sf my exciting command": <%= config.bin %> <%= command.id %> --name my:exciting:command - -# errors.InvalidDir - -This command must be run inside a plugin directory. diff --git a/package.json b/package.json index 4812f9b4..e1968dc5 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@salesforce/sf-plugins-core": "^11.1.1", "@salesforce/ts-types": "^2.0.10", "change-case": "^5.4.2", + "ejs": "^3.1.10", "fast-glob": "^3.3.2", "graphology": "^0.25.4", "graphology-types": "^0.24.7", diff --git a/src/commands/dev/generate/command.ts b/src/commands/dev/generate/command.ts index faacaedb..247173f5 100644 --- a/src/commands/dev/generate/command.ts +++ b/src/commands/dev/generate/command.ts @@ -5,13 +5,63 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +import { dirname, join, relative, sep } from 'node:path'; import { Messages } from '@salesforce/core'; import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; -import { fileExists, generate } from '../../../util.js'; +import defaultsDeep from 'lodash.defaultsdeep'; +import { pascalCase } from 'change-case'; +import { set } from '@salesforce/kit'; +import { get } from '@salesforce/ts-types'; +import { Generator } from '../../../generator.js'; +import { Topic } from '../../../types.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-dev', 'dev.generate.command'); +/** returns the modifications that need to be made for the oclif pjson topics information. Returns an empty object for "don't change anything" */ +export function addTopics( + newCmd: string, + existingTopics: Record, + commands: string[] = [] +): Record { + const updated: Record = {}; + + const paths = newCmd + .split(':') + // omit last word since it's not a topic, it's the command name + .slice(0, -1) + .map((_, index, array) => array.slice(0, index + 1).join('.')) + // reverse to build up the object from most specific to least + .reverse(); + + for (const p of paths) { + const pDepth = p.split('.').length; + // if new command if foo.bar and there are any commands in the foo topic, this should be marked external. + // if new command if foo.bar.baz and there are any commands in the foo.bar subtopic, it should be marked external. + const isExternal = commands.some((c) => c.split('.').slice(0, pDepth).join('.') === p); + const existing = get(updated, p); + if (existing) { + const merged = isExternal + ? { + external: true, + subtopics: existing, + } + : { + description: get(existingTopics, `${p}.description`, `description for ${p}`), + subtopics: existing, + }; + set(updated, p, merged); + } else { + const entry = isExternal + ? { external: true } + : { description: get(existingTopics, `${p}.description`, `description for ${p}`) }; + set(updated, p, entry); + } + } + + return updated; +} + export default class GenerateCommand extends SfCommand { public static readonly enableJsonFlag = false; public static readonly summary = messages.getMessage('summary'); @@ -27,6 +77,9 @@ export default class GenerateCommand extends SfCommand { force: Flags.boolean({ summary: messages.getMessage('flags.force.summary'), }), + 'dry-run': Flags.boolean({ + summary: messages.getMessage('flags.dry-run.summary'), + }), nuts: Flags.boolean({ summary: messages.getMessage('flags.nuts.summary'), allowNo: true, @@ -41,12 +94,88 @@ export default class GenerateCommand extends SfCommand { public async run(): Promise { const { flags } = await this.parse(GenerateCommand); - if (!(await fileExists('package.json'))) throw messages.createError('errors.InvalidDir'); - await generate('command', { - name: flags.name, + + const generator = await Generator.create({ force: flags.force, - nuts: flags.nuts, - unit: flags.unit, + dryRun: flags['dry-run'], }); + this.log(`Adding a command to ${generator.pjson.name}!`); + + if (Object.keys(generator.pjson.devDependencies).includes('@salesforce/plugin-command-reference')) { + // Get a list of all commands in `sf`. We will use this to determine if a topic is internal or external. + const commands = this.config.commandIDs.map((command) => command.replace(/:/g, '.').replace(/ /g, '.')); + const newTopics = addTopics(flags.name, generator.pjson.oclif.topics, commands); + defaultsDeep(generator.pjson.oclif.topics, newTopics); + } else { + const newTopics = addTopics(flags.name, generator.pjson.oclif.topics); + defaultsDeep(generator.pjson.oclif.topics, newTopics); + } + + await generator.writePjson(); + + const cmdPath = flags.name.split(':').join('/'); + const commandPath = `src/commands/${cmdPath}.ts`; + const className = pascalCase(flags.name); + const opts = { + className, + returnType: `${className}Result`, + commandPath, + year: new Date().getFullYear(), + pluginName: generator.pjson.name, + messageFile: flags.name.replace(/:/g, '.'), + }; + + // generate the command file + await generator.render( + generator.pjson.type === 'module' ? 'src/esm-command.ts.ejs' : 'src/cjs-command.ts.ejs', + commandPath, + opts + ); + + // generate the message file + await generator.render('messages/message.md.ejs', `messages/${flags.name.replace(/:/g, '.')}.md`); + + // generate the nuts file + if (flags.nuts) { + await generator.render('test/command.nut.ts.ejs', `test/commands/${cmdPath}.nut.ts`, { + cmd: flags.name.replace(/:/g, ' '), + year: new Date().getFullYear(), + pluginName: generator.pjson.name, + messageFile: flags.name.replace(/:/g, '.'), + }); + } + + // generate the unit test file + if (flags.unit) { + const unitPath = `test/commands/${cmdPath}.test.ts`; + const relativeCmdPath = relative(dirname(unitPath), commandPath).replace('.ts', '').replaceAll(sep, '/'); + await generator.render( + generator.pjson.type === 'module' ? 'test/esm-command.test.ts.ejs' : 'test/cjs-command.test.ts.ejs', + `test/commands/${cmdPath}.test.ts`, + { + className, + commandPath, + relativeCmdPath, + name: flags.name.replace(/:/g, ' '), + year: new Date().getFullYear(), + pluginName: generator.pjson.name, + } + ); + } + + // run the format, lint, and compile scripts + generator.execute('yarn format'); + generator.execute('yarn lint -- --fix'); + generator.execute('yarn compile'); + + const localExecutable = process.platform === 'win32' ? join('bin', 'dev.cmd') : join('bin', 'dev.js'); + + if (generator.pjson.scripts['test:deprecation-policy']) { + generator.execute(`${localExecutable} snapshot:generate`); + } + + if (generator.pjson.scripts['test:json-schema']) { + generator.execute(`${localExecutable} schema:generate`); + } } } diff --git a/src/generator.ts b/src/generator.ts new file mode 100644 index 00000000..3442ff58 --- /dev/null +++ b/src/generator.ts @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import { mkdir, readFile, writeFile } from 'node:fs/promises'; +import { dirname, join, relative } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { renderFile } from 'ejs'; +import { Ux } from '@salesforce/sf-plugins-core'; +import { Logger, SfError } from '@salesforce/core'; +import { AsyncOptionalCreatable } from '@salesforce/kit'; +import { colorize } from '@oclif/core/ux'; +import shelljs from 'shelljs'; +import { fileExists } from './util.js'; +import { PackageJson } from './types.js'; + +async function readPackageJson(): Promise { + try { + return JSON.parse(await readFile('package.json', 'utf-8')) as PackageJson; + } catch { + throw new SfError('This command must be run inside a plugin directory'); + } +} + +export class Generator extends AsyncOptionalCreatable { + public pjson!: PackageJson; + private templatesDir!: string; + private force: boolean | undefined; + private dryRun: boolean | undefined; + private ux = new Ux(); + private logger = Logger.childFromRoot('dev-generator'); + + public constructor(opts?: { dryRun?: boolean; force?: boolean }) { + super(opts); + this.dryRun = opts?.dryRun; + this.force = opts?.dryRun ?? opts?.force; + this.templatesDir = join(dirname(fileURLToPath(import.meta.url)), '../templates'); + this.logger = Logger.childFromRoot('dev-generator'); + this.logger.debug(`Templates directory: ${this.templatesDir}`); + } + + public async render(source: string, destination: string, data?: Record): Promise { + const fullSource = join(this.templatesDir, source); + const dryRunMsg = this.dryRun ? '[DRY RUN] ' : ''; + this.logger.debug(`${dryRunMsg}Rendering template ${fullSource} to ${destination}`); + + const rendered = await new Promise((resolve, reject) => { + renderFile(fullSource, data ?? {}, (err, str) => { + if (err) reject(err); + return resolve(str); + }); + }); + + let verb = 'Creating'; + if (rendered) { + const relativePath = relative(process.cwd(), destination); + if (await fileExists(destination)) { + const confirmation = + this.force ?? + (await ( + await import('@inquirer/confirm') + ).default({ + message: `Overwrite ${relativePath}?`, + })); + if (confirmation) { + verb = 'Overwriting'; + } else { + this.ux.log(`${dryRunMsg}${colorize('yellow', 'Skipping')} ${relativePath}`); + return; + } + } + + this.ux.log(`${dryRunMsg}${colorize('yellow', verb)} ${relativePath}`); + + if (!this.dryRun) { + await mkdir(dirname(destination), { recursive: true }); + await writeFile(destination, rendered); + } + } + } + + public execute(cmd: string): void { + if (this.dryRun) { + this.ux.log(`[DRY RUN] ${cmd}`); + return; + } + + this.logger.debug(`Executing command: ${cmd}`); + shelljs.exec(cmd); + } + + public async writePjson(): Promise { + const updating = colorize('yellow', 'Updating'); + if (this.dryRun) { + this.ux.log(`[DRY RUN] ${updating} package.json`); + return; + } + + this.ux.log(`${updating} package.json`); + await writeFile('package.json', JSON.stringify(this.pjson, null, 2)); + } + + protected async init(): Promise { + this.pjson = await readPackageJson(); + } +} diff --git a/src/generators/command.ts b/src/generators/command.ts deleted file mode 100644 index 7f776724..00000000 --- a/src/generators/command.ts +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2022, salesforce.com, inc. - * All rights reserved. - * Licensed under the BSD 3-Clause license. - * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import Generator from 'yeoman-generator'; -import { pascalCase } from 'change-case'; -import { set } from '@salesforce/kit'; -import { get } from '@salesforce/ts-types'; -import shelljs from 'shelljs'; -import defaultsDeep from 'lodash.defaultsdeep'; -import { PackageJson, Topic } from '../types.js'; - -export type CommandGeneratorOptions = { - name: string; - nuts: boolean; - unit: boolean; -} & Generator.GeneratorOptions - -const TEMPLATES_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../templates'); - -/** returns the modifications that need to be made for the oclif pjson topics information. Returns an empty object for "don't change anything" */ -export function addTopics( - newCmd: string, - existingTopics: Record, - commands: string[] = [] -): Record { - const updated: Record = {}; - - const paths = newCmd - .split(':') - // omit last word since it's not a topic, it's the command name - .slice(0, -1) - .map((_, index, array) => array.slice(0, index + 1).join('.')) - // reverse to build up the object from most specific to least - .reverse(); - - for (const p of paths) { - const pDepth = p.split('.').length; - // if new command if foo.bar and there are any commands in the foo topic, this should be marked external. - // if new command if foo.bar.baz and there are any commands in the foo.bar subtopic, it should be marked external. - const isExternal = commands.some((c) => c.split('.').slice(0, pDepth).join('.') === p); - const existing = get(updated, p); - if (existing) { - const merged = isExternal - ? { - external: true, - subtopics: existing, - } - : { - description: get(existingTopics, `${p}.description`, `description for ${p}`), - subtopics: existing, - }; - set(updated, p, merged); - } else { - const entry = isExternal - ? { external: true } - : { description: get(existingTopics, `${p}.description`, `description for ${p}`) }; - set(updated, p, entry); - } - } - - return updated; -} - -export default class Command extends Generator { - public declare options: CommandGeneratorOptions; - public pjson!: PackageJson; - - public constructor(args: string | string[], opts: CommandGeneratorOptions) { - super(args, opts); - } - - // eslint-disable-next-line @typescript-eslint/require-await - public async prompting(): Promise { - this.pjson = this.fs.readJSON('package.json') as unknown as PackageJson; - this.log(`Adding a command to ${this.pjson.name}!`); - - if (Object.keys(this.pjson.devDependencies).includes('@salesforce/plugin-command-reference')) { - // Get a list of all commands in `sf`. We will use this to determine if a topic is internal or external. - const sfCommandsStdout = shelljs.exec('sf commands --json', { silent: true }).stdout; - const commandsJson = JSON.parse(sfCommandsStdout) as Array<{ id: string }>; - const commands = commandsJson.map((command) => command.id.replace(/:/g, '.').replace(/ /g, '.')); - - const newTopics = addTopics(this.options.name, this.pjson.oclif.topics, commands); - defaultsDeep(this.pjson.oclif.topics, newTopics); - } else { - const newTopics = addTopics(this.options.name, this.pjson.oclif.topics); - defaultsDeep(this.pjson.oclif.topics, newTopics); - } - - this.fs.writeJSON('package.json', this.pjson); - } - - public writing(): void { - this.writeCmdFile(); - this.writeMessageFile(); - this.writeNutFile(); - this.writeUnitTestFile(); - } - - public end(): void { - shelljs.exec('yarn format'); - shelljs.exec('yarn lint -- --fix'); - shelljs.exec('yarn compile'); - - const localExecutable = process.platform === 'win32' ? path.join('bin', 'dev.cmd') : path.join('bin', 'dev.js'); - - if (this.pjson.scripts['test:deprecation-policy']) { - shelljs.exec(`${localExecutable} snapshot:generate`); - } - - if (this.pjson.scripts['test:json-schema']) { - shelljs.exec(`${localExecutable} schema:generate`); - } - } - - private getMessageFileName(): string { - return this.options.name.replace(/:/g, '.'); - } - - private writeCmdFile(): void { - this.sourceRoot(TEMPLATES_DIR); - const cmdPath = this.options.name.split(':').join('/'); - const commandPath = this.destinationPath(`src/commands/${cmdPath}.ts`); - const className = pascalCase(this.options.name); - const opts = { - ...this.options, - className, - returnType: `${className}Result`, - commandPath, - year: new Date().getFullYear(), - pluginName: this.pjson.name, - messageFile: this.getMessageFileName(), - }; - this.fs.copyTpl( - this.templatePath(this.pjson.type === 'module' ? 'src/esm-command.ts.ejs' : 'src/cjs-command.ts.ejs'), - commandPath, - opts - ); - } - - private writeMessageFile(): void { - this.sourceRoot(TEMPLATES_DIR); - const filename = this.getMessageFileName(); - const messagePath = this.destinationPath(`messages/${filename}.md`); - this.fs.copyTpl(this.templatePath('messages/message.md.ejs'), messagePath, this.options); - } - - private writeNutFile(): void { - if (!this.options.nuts) return; - this.sourceRoot(TEMPLATES_DIR); - const cmdPath = this.options.name.split(':').join('/'); - const nutPath = this.destinationPath(`test/commands/${cmdPath}.nut.ts`); - const opts = { - cmd: this.options.name.replace(/:/g, ' '), - year: new Date().getFullYear(), - pluginName: this.pjson.name, - messageFile: this.getMessageFileName(), - }; - this.fs.copyTpl(this.templatePath('test/command.nut.ts.ejs'), nutPath, opts); - } - - private writeUnitTestFile(): void { - if (!this.options.unit) return; - this.sourceRoot(TEMPLATES_DIR); - const cmdPath = this.options.name.split(':').join('/'); - const commandPath = this.destinationPath(`src/commands/${cmdPath}.ts`); - const className = pascalCase(this.options.name); - - const unitPath = this.destinationPath(`test/commands/${cmdPath}.test.ts`); - const relativeCmdPath = path - .relative(path.dirname(unitPath), commandPath) - .replace('.ts', '') - .replaceAll(path.sep, '/'); - this.fs.copyTpl( - this.templatePath(this.pjson.type === 'module' ? 'test/esm-command.test.ts.ejs' : 'test/cjs-command.test.ts.ejs'), - unitPath, - { - ...this.options, - className, - commandPath, - relativeCmdPath, - name: this.options.name.replace(/:/g, ' '), - year: new Date().getFullYear(), - } - ); - } -} From 33274d18d4ca933e2dc4a2785f4e02bc83ef3f99 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 26 Jul 2024 14:52:37 -0600 Subject: [PATCH 2/6] feat: generate library without yeoman --- messages/dev.generate.command.md | 6 +- src/commands/dev/generate/command.ts | 5 +- src/commands/dev/generate/library.ts | 98 ++++++++++++++++++++++++++-- src/generator.ts | 87 +++++++++++++++++++----- src/types.ts | 6 +- 5 files changed, 177 insertions(+), 25 deletions(-) diff --git a/messages/dev.generate.command.md b/messages/dev.generate.command.md index 1c2004bf..a9c76c71 100644 --- a/messages/dev.generate.command.md +++ b/messages/dev.generate.command.md @@ -28,10 +28,14 @@ Name of the new command. Use colons to separate the topic and command names. # flags.dry-run.summary -Output the generated files without writing them to disk. +Display the changes that would be made without writing them to disk. # examples - Generate the files for a new "sf my exciting command": <%= config.bin %> <%= command.id %> --name my:exciting:command + +# errors.InvalidDir + +This command must be run inside a plugin directory. diff --git a/src/commands/dev/generate/command.ts b/src/commands/dev/generate/command.ts index 247173f5..bb844fc4 100644 --- a/src/commands/dev/generate/command.ts +++ b/src/commands/dev/generate/command.ts @@ -95,10 +95,13 @@ export default class GenerateCommand extends SfCommand { public async run(): Promise { const { flags } = await this.parse(GenerateCommand); - const generator = await Generator.create({ + const generator = new Generator({ force: flags.force, dryRun: flags['dry-run'], }); + await generator.loadPjson(); + if (!generator.pjson) throw messages.createError('errors.InvalidDir'); + this.log(`Adding a command to ${generator.pjson.name}!`); if (Object.keys(generator.pjson.devDependencies).includes('@salesforce/plugin-command-reference')) { diff --git a/src/commands/dev/generate/library.ts b/src/commands/dev/generate/library.ts index d5f2304d..ae5c6d77 100644 --- a/src/commands/dev/generate/library.ts +++ b/src/commands/dev/generate/library.ts @@ -5,13 +5,18 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +import { join, resolve } from 'node:path'; import { Messages } from '@salesforce/core'; -import { SfCommand } from '@salesforce/sf-plugins-core'; -import { generate } from '../../../util.js'; +import { Flags, SfCommand } from '@salesforce/sf-plugins-core'; +import input from '@inquirer/input'; +import { Generator } from '../../../generator.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-dev', 'dev.generate.library'); +const containsInvalidChars = (i: string): boolean => + i.split('').some((part) => '!#$%^&*() ?/\\,.";\':|{}[]~`'.includes(part)); + export default class GenerateLibrary extends SfCommand { public static enableJsonFlag = false; public static readonly hidden = true; @@ -19,10 +24,93 @@ export default class GenerateLibrary extends SfCommand { public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); - public static readonly flags = {}; + public static readonly flags = { + 'dry-run': Flags.boolean(), + }; - // eslint-disable-next-line class-methods-use-this public async run(): Promise { - await generate('library', { force: true }); + const { flags } = await this.parse(GenerateLibrary); + this.log('Time to build a library!'); + + const generator = new Generator({ + dryRun: flags['dry-run'], + }); + + const answers = { + scope: await input({ + message: 'Npm Scope (should start with @)', + default: '@salesforce', + validate: (i: string): boolean | string => { + if (!i) return 'You must provide a scope.'; + if (!i.startsWith('@')) return 'Scope must start with @.'; + if (containsInvalidChars(i)) return 'Scope must not contain invalid characters.'; + if (i.length < 2) return 'Scope length must be greater than one'; + return true; + }, + }), + name: await input({ + message: 'Name', + validate: (i: string): boolean | string => { + if (!i) return 'You must provide a package name.'; + if (containsInvalidChars(i)) return 'Name must not contain invalid characters.'; + else return true; + }, + }), + description: await input({ message: 'Description' }), + org: await input({ + message: 'Github Org', + default: 'forcedotcom', + validate: (i: string): boolean | string => { + if (!i) return 'You must provide a Github Org.'; + if (containsInvalidChars(i)) return 'Github Org must not contain invalid characters.'; + else return true; + }, + }), + }; + + const directory = resolve(answers.name); + + generator.execute(`git clone git@github.com:forcedotcom/library-template.git ${directory}`); + + generator.cwd = join(process.cwd(), answers.name); + await generator.remove(resolve(directory, '.git')); + await generator.loadPjson(); + + generator.pjson.name = `${answers.scope}/${answers.name}`; + generator.pjson.description = answers.description; + generator.pjson.repository = `${answers.org}/${answers.name}`; + generator.pjson.homepage = `https://github.com/${answers.org}/${answers.name}`; + generator.pjson.bugs = { url: `https://github.com/${answers.org}/${answers.name}/issues` }; + + await generator.writePjson(); + + const cwd = `${process.cwd()}/${answers.name}`; + // Replace the message import + generator.replace({ + files: `${cwd}/src/hello.ts`, + from: /@salesforce\/library-template/g, + to: `${answers.scope}/${answers.name}`, + }); + + generator.replace({ + files: `${cwd}/**/*`, + from: /library-template/g, + to: answers.name, + }); + + generator.replace({ + files: `${cwd}/**/*`, + from: /forcedotcom/g, + to: answers.org, + }); + + generator.replace({ + files: `${cwd}/README.md`, + from: /@salesforce/g, + to: answers.scope, + }); + + generator.execute('yarn'); + generator.execute('yarn build'); } } diff --git a/src/generator.ts b/src/generator.ts index 3442ff58..da5b0801 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -5,36 +5,28 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -import { mkdir, readFile, writeFile } from 'node:fs/promises'; +import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; import { dirname, join, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; import { renderFile } from 'ejs'; import { Ux } from '@salesforce/sf-plugins-core'; -import { Logger, SfError } from '@salesforce/core'; -import { AsyncOptionalCreatable } from '@salesforce/kit'; +import { Logger } from '@salesforce/core'; import { colorize } from '@oclif/core/ux'; import shelljs from 'shelljs'; +import replace from 'replace-in-file'; import { fileExists } from './util.js'; import { PackageJson } from './types.js'; -async function readPackageJson(): Promise { - try { - return JSON.parse(await readFile('package.json', 'utf-8')) as PackageJson; - } catch { - throw new SfError('This command must be run inside a plugin directory'); - } -} - -export class Generator extends AsyncOptionalCreatable { +export class Generator { public pjson!: PackageJson; private templatesDir!: string; private force: boolean | undefined; private dryRun: boolean | undefined; private ux = new Ux(); private logger = Logger.childFromRoot('dev-generator'); + private workingDir: string = process.cwd(); public constructor(opts?: { dryRun?: boolean; force?: boolean }) { - super(opts); this.dryRun = opts?.dryRun; this.force = opts?.dryRun ?? opts?.force; this.templatesDir = join(dirname(fileURLToPath(import.meta.url)), '../templates'); @@ -42,6 +34,14 @@ export class Generator extends AsyncOptionalCreatable { this.logger.debug(`Templates directory: ${this.templatesDir}`); } + public get cwd(): string { + return this.workingDir; + } + + public set cwd(value: string) { + this.workingDir = value; + } + public async render(source: string, destination: string, data?: Record): Promise { const fullSource = join(this.templatesDir, source); const dryRunMsg = this.dryRun ? '[DRY RUN] ' : ''; @@ -89,7 +89,7 @@ export class Generator extends AsyncOptionalCreatable { } this.logger.debug(`Executing command: ${cmd}`); - shelljs.exec(cmd); + shelljs.exec(cmd, { cwd: this.cwd }); } public async writePjson(): Promise { @@ -100,10 +100,63 @@ export class Generator extends AsyncOptionalCreatable { } this.ux.log(`${updating} package.json`); - await writeFile('package.json', JSON.stringify(this.pjson, null, 2)); + await writeFile(join(this.cwd, 'package.json'), JSON.stringify(this.pjson, null, 2)); } - protected async init(): Promise { - this.pjson = await readPackageJson(); + public replace({ from, to, files }: { files: string; from: RegExp; to: string }): void { + const replacing = colorize('yellow', 'Replacing'); + if (this.dryRun) { + this.ux.log(`[DRY RUN] ${replacing} ${from} with ${to} in ${files}`); + return; + } + + this.logger.debug(`${replacing} ${from} with ${to} in ${files}`); + replace.sync({ + files, + from, + to, + }); + } + + public async remove(path: string): Promise { + const removing = colorize('yellow', 'Removing'); + if (this.dryRun) { + this.ux.log(`[DRY RUN] ${removing} ${path}`); + return; + } + + this.ux.log(`${removing} ${path}`); + await rm(path, { recursive: true }); + } + + public async loadPjson(): Promise { + try { + this.pjson = JSON.parse(await readFile(join(this.cwd, 'package.json'), 'utf-8')) as PackageJson; + } catch { + if (this.dryRun) { + this.pjson = { + name: '', + description: '', + dependencies: {}, + devDependencies: {}, + files: [], + bugs: '', + homepage: '', + repository: '', + oclif: { + bin: '', + topics: {}, + dirname: '', + }, + author: '', + // @ts-expect-error - not all properties are required + scripts: {}, + // @ts-expect-error - not all properties are required + wireit: {}, + }; + } + } + + return this.pjson; } } diff --git a/src/types.ts b/src/types.ts index a6ab3cff..847b3fc4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -35,7 +35,11 @@ export type PackageJson = { }; repository: string; homepage: string; - bugs: string; + bugs: + | string + | { + url: string; + }; author: string; description: string; scripts: { From 6cc0f887640097eb78cf3f08df6a8e6833e65bb5 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 26 Jul 2024 15:29:01 -0600 Subject: [PATCH 3/6] feat: generate plugin without yeoman --- messages/dev.generate.library.md | 4 + messages/dev.generate.plugin.md | 4 + messages/plugin.generator.md | 4 - package.json | 8 +- src/commands/dev/generate/library.ts | 9 +- src/commands/dev/generate/plugin.ts | 188 +++++++++++- src/generator.ts | 81 +++-- src/generators/library.ts | 124 -------- src/generators/plugin.ts | 186 ------------ src/util.ts | 8 - test/shared/commandGenerator.test.ts | 2 +- yarn.lock | 428 ++++++++++++++++++--------- 12 files changed, 540 insertions(+), 506 deletions(-) delete mode 100644 src/generators/library.ts delete mode 100644 src/generators/plugin.ts diff --git a/messages/dev.generate.library.md b/messages/dev.generate.library.md index 6d27187d..f9f99af3 100644 --- a/messages/dev.generate.library.md +++ b/messages/dev.generate.library.md @@ -11,3 +11,7 @@ When the command completes, your new library contains a few sample source and te # examples - <%= config.bin %> <%= command.id %> + +# flags.dry-run.summary + +Display the changes that would be made without writing them to disk. diff --git a/messages/dev.generate.plugin.md b/messages/dev.generate.plugin.md index 31291fe3..732478b0 100644 --- a/messages/dev.generate.plugin.md +++ b/messages/dev.generate.plugin.md @@ -11,3 +11,7 @@ When the command completes, your new plugin contains the source, message, and te # examples - <%= config.bin %> <%= command.id %> + +# flags.dry-run.summary + +Display the changes that would be made without writing them to disk. diff --git a/messages/plugin.generator.md b/messages/plugin.generator.md index c3780bc3..4c97e259 100644 --- a/messages/plugin.generator.md +++ b/messages/plugin.generator.md @@ -1,7 +1,3 @@ -# info.start - -Time to build an sf plugin! - # question.internal Are you building a plugin for an internal Salesforce team? diff --git a/package.json b/package.json index e1968dc5..b94e26c6 100644 --- a/package.json +++ b/package.json @@ -17,25 +17,25 @@ "change-case": "^5.4.2", "ejs": "^3.1.10", "fast-glob": "^3.3.2", + "got": "^13", "graphology": "^0.25.4", "graphology-types": "^0.24.7", "js-yaml": "^4.1.0", "lodash.defaultsdeep": "^4.6.1", + "proxy-agent": "^6.4.0", "replace-in-file": "^6.3.2", "shelljs": "^0.8.5", - "yarn-deduplicate": "^6.0.2", - "yeoman-environment": "^3.19.3", - "yeoman-generator": "^5.10.0" + "yarn-deduplicate": "^6.0.2" }, "devDependencies": { "@oclif/plugin-command-snapshot": "^5.2.3", "@salesforce/cli-plugins-testkit": "^5.3.15", "@salesforce/dev-scripts": "^10.2.4", "@salesforce/plugin-command-reference": "^3.1.5", + "@types/ejs": "^3.1.5", "@types/js-yaml": "^4.0.5", "@types/lodash.defaultsdeep": "^4.6.9", "@types/shelljs": "^0.8.14", - "@types/yeoman-generator": "^5.2.14", "eslint-plugin-sf-plugin": "^1.18.8", "oclif": "^4.4.17", "strip-ansi": "^7.1.0", diff --git a/src/commands/dev/generate/library.ts b/src/commands/dev/generate/library.ts index ae5c6d77..6143c622 100644 --- a/src/commands/dev/generate/library.ts +++ b/src/commands/dev/generate/library.ts @@ -25,12 +25,14 @@ export default class GenerateLibrary extends SfCommand { public static readonly examples = messages.getMessages('examples'); public static readonly flags = { - 'dry-run': Flags.boolean(), + 'dry-run': Flags.boolean({ + summary: messages.getMessage('flags.dry-run.summary'), + }), }; public async run(): Promise { const { flags } = await this.parse(GenerateLibrary); - this.log('Time to build a library!'); + this.log(`Time to build a library!${flags['dry-run'] ? ' (dry-run)' : ''}`); const generator = new Generator({ dryRun: flags['dry-run'], @@ -73,7 +75,8 @@ export default class GenerateLibrary extends SfCommand { generator.execute(`git clone git@github.com:forcedotcom/library-template.git ${directory}`); generator.cwd = join(process.cwd(), answers.name); - await generator.remove(resolve(directory, '.git')); + await generator.remove('.git'); + generator.execute('git init'); await generator.loadPjson(); generator.pjson.name = `${answers.scope}/${answers.name}`; diff --git a/src/commands/dev/generate/plugin.ts b/src/commands/dev/generate/plugin.ts index 7869a702..f125a1ff 100644 --- a/src/commands/dev/generate/plugin.ts +++ b/src/commands/dev/generate/plugin.ts @@ -5,12 +5,71 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +import { join, resolve } from 'node:path'; +import { createRequire } from 'node:module'; import { Messages } from '@salesforce/core'; -import { SfCommand } from '@salesforce/sf-plugins-core'; -import { generate } from '../../../util.js'; +import { Flags, SfCommand } from '@salesforce/sf-plugins-core'; +import { ProxyAgent } from 'proxy-agent'; +import shelljs from 'shelljs'; +import got from 'got'; +import confirm from '@inquirer/confirm'; +import input from '@inquirer/input'; +import select from '@inquirer/select'; +import { Generator } from '../../../generator.js'; +import { validatePluginName } from '../../../util.js'; +import { stringToChoice } from '../../../prompts/functions.js'; +import { NYC, PackageJson } from '../../../types.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-dev', 'dev.generate.plugin'); +const generateMessages = Messages.loadMessages('@salesforce/plugin-dev', 'plugin.generator'); + +async function fetchGithubUserFromAPI(): Promise<{ login: string; name: string } | undefined> { + const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN; + if (!token) return; + + const headers = { + Accept: 'application/vnd.github.v3+json', + Authorization: `Bearer ${token}`, + }; + + try { + const { login, name } = await got('https://api.github.com/user', { + headers, + agent: { https: new ProxyAgent() }, + }).json<{ + login: string; + name: string; + }>(); + return { login, name }; + } catch { + // ignore + } +} + +function fetchGithubUserFromGit(): string | undefined { + try { + const result = shelljs.exec('git config --get user.name', { silent: true }); + return result.stdout.trim(); + } catch { + // ignore + } +} + +async function fetchGithubUser(): Promise<{ login?: string; name: string | undefined } | undefined> { + return (await fetchGithubUserFromAPI()) ?? { name: fetchGithubUserFromGit() }; +} + +function determineDefaultAuthor( + user: { login?: string; name: string | undefined } | undefined, + defaultValue: string +): string { + const { login, name } = user ?? { login: undefined, name: undefined }; + if (name && login) return `${name} @${login}`; + if (name) return name; + if (login) return `@${login}`; + return defaultValue; +} export default class GeneratePlugin extends SfCommand { public static enableJsonFlag = false; @@ -19,10 +78,129 @@ export default class GeneratePlugin extends SfCommand { public static readonly examples = messages.getMessages('examples'); public static readonly aliases = ['plugins:generate']; public static readonly deprecateAliases = true; - public static readonly flags = {}; + public static readonly flags = { + 'dry-run': Flags.boolean({ + summary: messages.getMessage('flags.dry-run.summary'), + }), + }; - // eslint-disable-next-line class-methods-use-this public async run(): Promise { - await generate('plugin', { force: true }); + const { flags } = await this.parse(GeneratePlugin); + this.log(`Time to build a plugin!${flags['dry-run'] ? ' (dry-run)' : ''}`); + + const generator = new Generator({ + dryRun: flags['dry-run'], + }); + + const githubUsername = await fetchGithubUser(); + const internal = await confirm({ message: generateMessages.getMessage('question.internal') }); + + const answers = { + internal, + name: await input({ + message: generateMessages.getMessage(internal ? 'question.internal.name' : 'question.external.name'), + validate: (i: string): boolean | string => { + const result = validatePluginName(i, internal ? '2PP' : '3PP'); + if (result) return true; + + return generateMessages.getMessage(internal ? 'error.Invalid2ppName' : 'error.Invalid3ppName'); + }, + }), + description: await input({ message: generateMessages.getMessage('question.description') }), + ...(!internal + ? { + author: await input({ + message: generateMessages.getMessage('question.author'), + default: determineDefaultAuthor(githubUsername, 'Author Name'), + }), + codeCoverage: await select({ + message: generateMessages.getMessage('question.code-coverage'), + default: '50%', + choices: ['0%', '25%', '50%', '75%', '90%', '100%'].map(stringToChoice), + }), + } + : {}), + }; + + const directory = resolve(answers.name); + const templateRepo = answers.internal + ? 'git clone https://github.com/salesforcecli/plugin-template-sf.git' + : 'git clone https://github.com/salesforcecli/plugin-template-sf-external.git'; + + generator.execute(`${templateRepo} "${directory}"`); + + generator.cwd = directory; + await generator.remove('.git'); + await generator.loadPjson(); + + generator.execute('git init'); + + const updated: Partial = answers.internal + ? { + name: `@salesforce/${answers.name}`, + repository: `salesforcecli/${answers.name}`, + homepage: `https://github.com/salesforcecli/${answers.name}`, + description: answers.description, + } + : { + name: answers.name, + description: answers.description, + }; + + if (answers.author) { + updated.author = answers.author; + } + + generator.pjson = { + ...generator.pjson, + ...updated, + }; + + await generator.writePjson(); + + if (!answers.internal && answers.codeCoverage) { + const nycConfig = await generator.readJson('.nycrc'); + const codeCoverage = Number.parseInt(answers.codeCoverage.replace('%', ''), 10); + nycConfig['check-coverage'] = true; + nycConfig.lines = codeCoverage; + nycConfig.statements = codeCoverage; + nycConfig.functions = codeCoverage; + nycConfig.branches = codeCoverage; + await generator.writeJson('.nycrc', nycConfig); + } + + generator.replace({ + files: `${generator.cwd}/**/*`, + from: answers.internal ? /plugin-template-sf/g : /plugin-template-sf-external/g, + to: answers.name, + }); + + if (!answers.internal) { + await generator.remove('CODE_OF_CONDUCT.md'); + await generator.remove('LICENSE.txt'); + } + + try { + // Try to dedupe yarn.lock before installing dependencies. + const require = createRequire(import.meta.url); + const yarnDedupePath = require.resolve('.bin/yarn-deduplicate'); + generator.execute(yarnDedupePath); + } catch { + // do nothing + } + + try { + generator.execute('yarn install'); + } catch (e) { + // Run yarn install in case dev-scripts detected changes during yarn build. + generator.execute('yarn install'); + } + + generator.execute('yarn build'); + + if (answers.internal) { + const dev = process.platform === 'win32' ? 'dev.cmd' : 'dev.js'; + generator.execute(`${join(resolve(generator.cwd), 'bin', dev)} schema generate`); + } } } diff --git a/src/generator.ts b/src/generator.ts index da5b0801..d936917c 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -92,6 +92,37 @@ export class Generator { shelljs.exec(cmd, { cwd: this.cwd }); } + public async loadPjson(): Promise { + try { + this.pjson = JSON.parse(await readFile(join(this.cwd, 'package.json'), 'utf-8')) as PackageJson; + } catch { + if (this.dryRun) { + this.pjson = { + name: '', + description: '', + dependencies: {}, + devDependencies: {}, + files: [], + bugs: '', + homepage: '', + repository: '', + oclif: { + bin: '', + topics: {}, + dirname: '', + }, + author: '', + // @ts-expect-error - not all properties are required + scripts: {}, + // @ts-expect-error - not all properties are required + wireit: {}, + }; + } + } + + return this.pjson; + } + public async writePjson(): Promise { const updating = colorize('yellow', 'Updating'); if (this.dryRun) { @@ -119,44 +150,34 @@ export class Generator { } public async remove(path: string): Promise { + const fullPath = join(this.cwd, path); const removing = colorize('yellow', 'Removing'); if (this.dryRun) { - this.ux.log(`[DRY RUN] ${removing} ${path}`); + this.ux.log(`[DRY RUN] ${removing} ${fullPath}`); return; } - this.ux.log(`${removing} ${path}`); - await rm(path, { recursive: true }); + this.ux.log(`${removing} ${fullPath}`); + await rm(fullPath, { recursive: true, force: true }); } - public async loadPjson(): Promise { - try { - this.pjson = JSON.parse(await readFile(join(this.cwd, 'package.json'), 'utf-8')) as PackageJson; - } catch { - if (this.dryRun) { - this.pjson = { - name: '', - description: '', - dependencies: {}, - devDependencies: {}, - files: [], - bugs: '', - homepage: '', - repository: '', - oclif: { - bin: '', - topics: {}, - dirname: '', - }, - author: '', - // @ts-expect-error - not all properties are required - scripts: {}, - // @ts-expect-error - not all properties are required - wireit: {}, - }; - } + public async readJson(path: string): Promise { + if (this.dryRun) { + return {} as T; } - return this.pjson; + return JSON.parse(await readFile(join(this.cwd, path), 'utf-8')) as T; + } + + public async writeJson(path: string, data: T): Promise { + const fullPath = join(this.cwd, path); + const writing = colorize('yellow', 'Writing'); + if (this.dryRun) { + this.ux.log(`[DRY RUN] ${writing} to ${fullPath}`); + return; + } + + this.ux.log(`${writing} to ${fullPath}`); + await writeFile(fullPath, JSON.stringify(data, null, 2)); } } diff --git a/src/generators/library.ts b/src/generators/library.ts deleted file mode 100644 index a3a19392..00000000 --- a/src/generators/library.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2022, salesforce.com, inc. - * All rights reserved. - * Licensed under the BSD 3-Clause license. - * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import Generator from 'yeoman-generator'; -import shelljs from 'shelljs'; -import replace from 'replace-in-file'; -import input from '@inquirer/input'; -import { PackageJson } from '../types.js'; -import { readJson } from '../util.js'; - -type Answers = { - name: string; - description: string; - org: string; - scope: string; -}; - -const containsInvalidChars = (i: string): boolean => - i.split('').some((part) => '!#$%^&*() ?/\\,.";\':|{}[]~`'.includes(part)); - -const TEMPLATES_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../templates'); - -export default class Library extends Generator { - private answers!: Answers; - - public constructor(args: string | string[], opts: Generator.GeneratorOptions) { - super(args, opts); - this.env.options.nodePackageManager = 'yarn'; - } - - public async prompting(): Promise { - this.log('Time to build a library!'); - - this.answers = { - scope: await input({ - message: 'Npm Scope (should start with @)', - default: '@salesforce', - validate: (i: string): boolean | string => { - if (!i) return 'You must provide a scope.'; - if (!i.startsWith('@')) return 'Scope must start with @.'; - if (containsInvalidChars(i)) return 'Scope must not contain invalid characters.'; - if (i.length < 2) return 'Scope length must be greater than one'; - return true; - }, - }), - name: await input({ - message: 'Name', - validate: (i: string): boolean | string => { - if (!i) return 'You must provide a package name.'; - if (containsInvalidChars(i)) return 'Name must not contain invalid characters.'; - else return true; - }, - }), - description: await input({ message: 'Description' }), - org: await input({ - message: 'Github Org', - default: 'forcedotcom', - validate: (i: string): boolean | string => { - if (!i) return 'You must provide a Github Org.'; - if (containsInvalidChars(i)) return 'Github Org must not contain invalid characters.'; - else return true; - }, - }), - }; - - const directory = path.resolve(this.answers.name); - shelljs.exec(`git clone git@github.com:forcedotcom/library-template.git ${directory}`); - fs.rmSync(`${path.resolve(this.answers.name, '.git')}`, { recursive: true }); - this.destinationRoot(directory); - this.env.cwd = this.destinationPath(); - } - - public writing(): void { - const pjson = readJson(path.join(this.env.cwd, 'package.json')); - - this.sourceRoot(TEMPLATES_DIR); - - const updated = { - name: `${this.answers.scope}/${this.answers.name}`, - repository: `${this.answers.org}/${this.answers.name}`, - homepage: `https://github.com/${this.answers.org}/${this.answers.name}`, - description: this.answers.description, - bugs: { url: `https://github.com/${this.answers.org}/${this.answers.name}/issues` }, - }; - const final = Object.assign({}, pjson, updated); - this.fs.writeJSON(this.destinationPath('./package.json'), final); - - // Replace the message import - replace.sync({ - files: `${this.env.cwd}/src/hello.ts`, - from: /@salesforce\/library-template/g, - to: `${this.answers.scope}/${this.answers.name}`, - }); - - replace.sync({ - files: `${this.env.cwd}/**/*`, - from: /library-template/g, - to: this.answers.name, - }); - - replace.sync({ - files: `${this.env.cwd}/**/*`, - from: /forcedotcom/g, - to: this.answers.org, - }); - - replace.sync({ - files: `${this.env.cwd}/README.md`, - from: /@salesforce/g, - to: this.answers.scope, - }); - } - - public end(): void { - shelljs.exec('yarn build', { cwd: this.env.cwd }); - } -} diff --git a/src/generators/plugin.ts b/src/generators/plugin.ts deleted file mode 100644 index d38862a0..00000000 --- a/src/generators/plugin.ts +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2022, salesforce.com, inc. - * All rights reserved. - * Licensed under the BSD 3-Clause license. - * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause - */ - -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { createRequire } from 'node:module'; -import Generator from 'yeoman-generator'; -import shelljs from 'shelljs'; -import replace from 'replace-in-file'; -import { Messages } from '@salesforce/core'; -import confirm from '@inquirer/confirm'; -import input from '@inquirer/input'; -import select from '@inquirer/select'; -import { stringToChoice } from '../prompts/functions.js'; -import { NYC, PackageJson } from '../types.js'; -import { readJson, validatePluginName } from '../util.js'; - -Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); -const messages = Messages.loadMessages('@salesforce/plugin-dev', 'plugin.generator'); - -const TEMPLATES_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../templates'); - -type PluginAnswers = { - internal: boolean; - name: string; - description: string; - author?: string; - codeCoverage?: string; -}; - -function rm(filepath: string, options: { recursive?: boolean }): void { - try { - fs.rmSync(filepath, options); - } catch { - // do nothing - } -} - -/** - * See https://yeoman.io/authoring/running-context.html for the overridable methods - */ -export default class Plugin extends Generator { - private answers!: PluginAnswers; - private githubUsername?: string; - - public constructor(args: string | string[], opts: Generator.GeneratorOptions) { - super(args, opts); - this.env.options.nodePackageManager = 'yarn'; - } - - public async prompting(): Promise { - this.log(messages.getMessage('info.start')); - - this.githubUsername = await this.getGitUsername(); - - const internal = await confirm({ message: messages.getMessage('question.internal') }); - this.answers = { - internal, - name: await input({ - message: messages.getMessage(internal ? 'question.internal.name' : 'question.external.name'), - validate: (i: string): boolean | string => { - const result = validatePluginName(i, internal ? '2PP' : '3PP'); - if (result) return true; - - return messages.getMessage(internal ? 'error.Invalid2ppName' : 'error.Invalid3ppName'); - }, - }), - description: await input({ message: messages.getMessage('question.description') }), - ...(!internal - ? { - author: await input({ - message: messages.getMessage('question.author'), - default: this.githubUsername, - }), - codeCoverage: await select({ - message: messages.getMessage('question.code-coverage'), - default: '50%', - choices: ['0%', '25%', '50%', '75%', '90%', '100%'].map(stringToChoice), - }), - } - : {}), - }; - - const directory = path.resolve(this.answers.name); - - const templateRepo = this.answers.internal - ? 'git clone https://github.com/salesforcecli/plugin-template-sf.git' - : 'git clone https://github.com/salesforcecli/plugin-template-sf-external.git'; - shelljs.exec(`${templateRepo} "${directory}"`); - try { - fs.rmSync(`${path.resolve(this.answers.name, '.git')}`, { recursive: true }); - } catch { - // ignore - } - - this.destinationRoot(directory); - this.env.cwd = this.destinationPath(); - - shelljs.exec('git init', { cwd: this.env.cwd }); - } - - public writing(): void { - const pjson = readJson(path.normalize(path.join(this.env.cwd, 'package.json'))); - - this.sourceRoot(TEMPLATES_DIR); - - const updated: Partial = this.answers.internal - ? { - name: `@salesforce/${this.answers.name}`, - repository: `salesforcecli/${this.answers.name}`, - homepage: `https://github.com/salesforcecli/${this.answers.name}`, - description: this.answers.description, - } - : { - name: this.answers.name, - description: this.answers.description, - }; - - if (this.answers.author) updated.author = this.answers.author; - - const final = Object.assign({}, pjson, updated); - - if (!this.answers.internal && this.answers.codeCoverage) { - const nycConfig = readJson(path.join(this.env.cwd, '.nycrc')); - const codeCoverage = Number.parseInt(this.answers.codeCoverage.replace('%', ''), 10); - nycConfig['check-coverage'] = true; - nycConfig.lines = codeCoverage; - nycConfig.statements = codeCoverage; - nycConfig.functions = codeCoverage; - nycConfig.branches = codeCoverage; - this.fs.writeJSON(this.destinationPath('.nycrc'), nycConfig); - } - - this.fs.writeJSON(this.destinationPath('./package.json'), final); - - replace.sync({ - files: `${this.env.cwd}/**/*`, - from: this.answers.internal ? /plugin-template-sf/g : /plugin-template-sf-external/g, - to: this.answers.name, - }); - - if (!this.answers.internal) { - rm(path.join(this.env.cwd, 'CODE_OF_CONDUCT.md'), { recursive: true }); - rm(path.join(this.env.cwd, 'LICENSE.txt'), { recursive: true }); - } - } - - public install(): void { - try { - // Try to dedupe yarn.lock before installing dependencies. - const require = createRequire(import.meta.url); - const yarnDedupePath = require.resolve('.bin/yarn-deduplicate'); - shelljs.exec(yarnDedupePath, { cwd: this.env.cwd }); - } catch { - // do nothing - } - - try { - shelljs.exec('yarn install', { cwd: this.env.cwd }); - } catch (e) { - // Run yarn install in case dev-scripts detected changes during yarn build. - shelljs.exec('yarn install', { cwd: this.env.cwd }); - } - } - public end(): void { - shelljs.exec('yarn build', { cwd: this.env.cwd }); - - if (this.answers.internal) { - const dev = process.platform === 'win32' ? 'dev.cmd' : 'dev.js'; - shelljs.exec(`${path.join(path.resolve(this.env.cwd), 'bin', dev)} schema generate`, { cwd: this.env.cwd }); - } - } - - private async getGitUsername(): Promise { - try { - return await this.user.github.username(); - } catch { - return; - } - } -} diff --git a/src/util.ts b/src/util.ts index 04c62679..03b59eb1 100644 --- a/src/util.ts +++ b/src/util.ts @@ -6,16 +6,8 @@ */ import fs from 'node:fs'; import os from 'node:os'; -import { createRequire } from 'node:module'; -import yeoman from 'yeoman-environment'; import { FlagAnswers } from './types.js'; -export function generate(type: string, generatorOptions: Record = {}): Promise { - const env = yeoman.createEnv(); - env.register(createRequire(import.meta.url).resolve(`./generators/${type}`), `sf:${type}`); - return env.run(`sf:${type}`, generatorOptions); -} - export function readJson(filePath: string): T { const pjsonRaw = fs.readFileSync(filePath, 'utf-8'); return JSON.parse(pjsonRaw) as T; diff --git a/test/shared/commandGenerator.test.ts b/test/shared/commandGenerator.test.ts index 6463579a..991f7f19 100644 --- a/test/shared/commandGenerator.test.ts +++ b/test/shared/commandGenerator.test.ts @@ -6,7 +6,7 @@ */ import { expect } from 'chai'; -import { addTopics } from '../../src/generators/command.js'; +import { addTopics } from '../../src/commands/dev/generate/command.js'; import { Topic } from '../../src/types.js'; describe('command generator', () => { diff --git a/yarn.lock b/yarn.lock index 59f855b6..c4e659d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2010,6 +2010,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@sindresorhus/is@^5.2.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" + integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== + "@sindresorhus/merge-streams@^2.1.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" @@ -2544,6 +2549,13 @@ dependencies: defer-to-connect "^2.0.0" +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2554,6 +2566,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@tootallnate/quickjs-emscripten@^0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" + integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -2609,22 +2626,10 @@ dependencies: "@types/node" "*" -"@types/debug@*": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" - integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg== - dependencies: - "@types/ms" "*" - -"@types/diff@*": - version "5.0.3" - resolved "https://registry.yarnpkg.com/@types/diff/-/diff-5.0.3.tgz#1f89e49ff83b5d200d78964fb896c68498ce1828" - integrity sha512-amrLbRqTU9bXMCc6uX0sWpxsQzRIo9z6MJPkH1pkez/qOxuqSZVuryJAWoBRq94CeG8JxY+VK4Le9HtjQR5T9A== - -"@types/ejs@*": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.2.tgz#75d277b030bc11b3be38c807e10071f45ebc78d9" - integrity sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g== +"@types/ejs@^3.1.5": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.5.tgz#49d738257cc73bafe45c13cb8ff240683b4d5117" + integrity sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg== "@types/expect@^1.20.4": version "1.20.4" @@ -2651,20 +2656,17 @@ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== -"@types/inquirer@^8": - version "8.2.10" - resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-8.2.10.tgz#9444dce2d764c35bc5bb4d742598aaa4acb6561b" - integrity sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA== - dependencies: - "@types/through" "*" - rxjs "^7.2.0" +"@types/http-cache-semantics@^4.0.2": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== "@types/js-yaml@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== -"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.15": +"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -2693,26 +2695,6 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.194.tgz#b71eb6f7a0ff11bff59fc987134a093029258a76" integrity sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g== -"@types/mem-fs-editor@*": - version "7.0.3" - resolved "https://registry.yarnpkg.com/@types/mem-fs-editor/-/mem-fs-editor-7.0.3.tgz#cc5f3bc87d55a9e82d4de7f91fd7b53cc4b6e0fe" - integrity sha512-ZvW3LwmY2A44EZHOmpG44OpfumpVkjp1TqXKsh9T/+yL7xlQu5ZO7eP8GQhSL/JW5KXbJKz3CMP12QKzrjSXWg== - dependencies: - "@types/ejs" "*" - "@types/json-schema" "*" - "@types/mem-fs" "*" - "@types/node" "*" - "@types/vinyl" "*" - globby "^11.1.0" - -"@types/mem-fs@*": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@types/mem-fs/-/mem-fs-1.1.2.tgz#e7e4cc741225dea109b5f3ceb519b45f353e1393" - integrity sha512-tt+4IoDO8/wmtaP2bHnB91c8AnzYtR9MK6NxfcZY9E3XgtmzOiFMeSXu3EZrBeevd0nJ87iGoUiFDGsb9QUvew== - dependencies: - "@types/node" "*" - "@types/vinyl" "*" - "@types/minimatch@*": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" @@ -2733,11 +2715,6 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f" integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw== -"@types/ms@*": - version "0.7.31" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" - integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== - "@types/mute-stream@^0.0.4": version "0.0.4" resolved "https://registry.yarnpkg.com/@types/mute-stream/-/mute-stream-0.0.4.tgz#77208e56a08767af6c5e1237be8888e2f255c478" @@ -2801,24 +2778,12 @@ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz#bf2e02a3dbd4aecaf95942ecd99b7402e03fad5e" integrity sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA== -"@types/text-table@*": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@types/text-table/-/text-table-0.2.2.tgz#774c90cfcfbc8b4b0ebb00fecbe861dc8b1e8e26" - integrity sha512-dGoI5Af7To0R2XE8wJuc6vwlavWARsCh3UKJPjWs1YEqGUqfgBI/j/4GX0yf19/DsDPPf0YAXWAp8psNeIehLg== - -"@types/through@*": - version "0.0.30" - resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895" - integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg== - dependencies: - "@types/node" "*" - "@types/unist@*": version "3.0.2" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.2.tgz#6dd61e43ef60b34086287f83683a5c1b2dc53d20" integrity sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ== -"@types/vinyl@*", "@types/vinyl@^2.0.4": +"@types/vinyl@^2.0.4": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/vinyl/-/vinyl-2.0.7.tgz#9739a9a2afaf9af32761c54a0e82c735279f726c" integrity sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg== @@ -2831,35 +2796,6 @@ resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== -"@types/yeoman-environment@*": - version "2.10.8" - resolved "https://registry.yarnpkg.com/@types/yeoman-environment/-/yeoman-environment-2.10.8.tgz#f9e9fea143ecd6e3358d759b366fddf869aa4e7a" - integrity sha512-/g92Z/PAMXklSoWafGxTW8DxB4admgl5NDHvKn0qMkz2C0GJUvbV7tpU9LbKNnlMO+ynerz5bCVbhuBzEHbb6Q== - dependencies: - "@types/diff" "*" - "@types/inquirer" "^8" - "@types/mem-fs" "*" - "@types/text-table" "*" - "@types/vinyl" "*" - "@types/yeoman-generator" "*" - chalk "^4.1.0" - commander "^9.0.0" - execa "^5.0.0" - rxjs "^6.4.0" - -"@types/yeoman-generator@*", "@types/yeoman-generator@^5.2.14": - version "5.2.14" - resolved "https://registry.yarnpkg.com/@types/yeoman-generator/-/yeoman-generator-5.2.14.tgz#31bc71dfaf2a11885a41924779a5e8abd2d1612f" - integrity sha512-eIYBqQyURXiAaoU6jvJqMI+tNSG4s7EXtcHucLCgb8EV2vqz4x1WPr91MT0MiWHV8+9dDRrMkc1VZ6LduexuyA== - dependencies: - "@types/debug" "*" - "@types/ejs" "*" - "@types/inquirer" "^8" - "@types/mem-fs-editor" "*" - "@types/node" "*" - "@types/yeoman-environment" "*" - rxjs "^6.4.0" - "@typescript-eslint/eslint-plugin@^6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3" @@ -3005,6 +2941,13 @@ agent-base@^7.0.2: dependencies: debug "^4.3.4" +agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + agentkeepalive@^4.1.3, agentkeepalive@^4.2.1: version "4.3.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" @@ -3261,6 +3204,13 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +ast-types@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -3313,6 +3263,11 @@ base64url@^3.0.1: resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== +basic-ftp@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" + integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== + before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -3512,6 +3467,24 @@ cacheable-lookup@^5.0.3: resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.8: + version "10.2.14" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.14.tgz#eb915b665fda41b79652782df3f553449c406b9d" + integrity sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ== + dependencies: + "@types/http-cache-semantics" "^4.0.2" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.3" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" + cacheable-request@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" @@ -3895,11 +3868,6 @@ commander@^12.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== -commander@^9.0.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" - integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== - comment-parser@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.1.tgz#bdafead37961ac079be11eb7ec65c4d021eaf9cc" @@ -4042,6 +4010,11 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== +data-uri-to-buffer@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b" + integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== + dateformat@^4.5.0, dateformat@^4.6.3: version "4.6.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" @@ -4122,7 +4095,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -defer-to-connect@^2.0.0: +defer-to-connect@^2.0.0, defer-to-connect@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== @@ -4144,6 +4117,15 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +degenerator@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" + integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== + dependencies: + ast-types "^0.13.4" + escodegen "^2.1.0" + esprima "^4.0.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -4423,6 +4405,17 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + eslint-config-prettier@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" @@ -4609,7 +4602,7 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -4904,6 +4897,11 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== + form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -4927,6 +4925,15 @@ fs-extra@^11.0.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^8.1, fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -5061,7 +5068,7 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -5074,6 +5081,16 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-uri@^6.0.1: + version "6.0.3" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.3.tgz#0d26697bc13cf91092e519aa63aa60ee5b6f385a" + integrity sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw== + dependencies: + basic-ftp "^5.0.2" + data-uri-to-buffer "^6.0.2" + debug "^4.3.4" + fs-extra "^11.2.0" + git-raw-commits@^2.0.11: version "2.0.11" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723" @@ -5219,6 +5236,23 @@ got@^11: p-cancelable "^2.0.0" responselike "^2.0.0" +got@^13: + version "13.0.0" + resolved "https://registry.yarnpkg.com/got/-/got-13.0.0.tgz#a2402862cef27a5d0d1b07c0fb25d12b58175422" + integrity sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" + decompress-response "^6.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" + graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -5422,6 +5456,14 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" +http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" @@ -5430,6 +5472,14 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.2.0" + https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -5446,6 +5496,14 @@ https-proxy-agent@^7.0.1: agent-base "^7.0.2" debug "4" +https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -5592,6 +5650,14 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" + ip@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" @@ -5980,6 +6046,11 @@ js2xmlparser@^4.0.1: dependencies: xmlcreate "^2.0.4" +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + jsdoc-type-pratt-parser@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz#136f0571a99c184d84ec84662c45c29ceff71114" @@ -6148,6 +6219,13 @@ keyv@^4.0.0: dependencies: json-buffer "3.0.1" +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -6382,6 +6460,11 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -6396,7 +6479,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: +lru-cache@^7.14.1, lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1: version "7.18.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== @@ -6618,6 +6701,11 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -6875,6 +6963,11 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +netmask@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + nise@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/nise/-/nise-4.1.0.tgz#8fb75a26e90b99202fa1e63f448f58efbcdedaf6" @@ -7011,6 +7104,11 @@ normalize-url@^6.0.1: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +normalize-url@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.1.tgz#9b7d96af9836577c58f5883e939365fa15623a4a" + integrity sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w== + npm-bundled@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" @@ -7338,6 +7436,11 @@ p-cancelable@^2.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -7413,6 +7516,28 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pac-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz#0fb02496bd9fb8ae7eb11cfd98386daaac442f58" + integrity sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg== + dependencies: + "@tootallnate/quickjs-emscripten" "^0.23.0" + agent-base "^7.0.2" + debug "^4.3.4" + get-uri "^6.0.1" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.5" + pac-resolver "^7.0.1" + socks-proxy-agent "^8.0.4" + +pac-resolver@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6" + integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg== + dependencies: + degenerator "^5.0.0" + netmask "^2.0.2" + package-hash@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" @@ -7787,6 +7912,25 @@ proper-lockfile@^4.1.2: retry "^0.12.0" signal-exit "^3.0.2" +proxy-agent@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d" + integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ== + dependencies: + agent-base "^7.0.2" + debug "^4.3.4" + http-proxy-agent "^7.0.1" + https-proxy-agent "^7.0.3" + lru-cache "^7.14.1" + pac-proxy-agent "^7.0.1" + proxy-from-env "^1.1.0" + socks-proxy-agent "^8.0.2" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -8041,7 +8185,7 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== -resolve-alpn@^1.0.0: +resolve-alpn@^1.0.0, resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== @@ -8079,6 +8223,13 @@ responselike@^2.0.0: dependencies: lowercase-keys "^2.0.0" +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== + dependencies: + lowercase-keys "^3.0.0" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -8126,14 +8277,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^6.4.0: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -rxjs@^7.2.0, rxjs@^7.5.5: +rxjs@^7.5.5: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== @@ -8419,6 +8563,15 @@ socks-proxy-agent@^7.0.0: debug "^4.3.3" socks "^2.6.2" +socks-proxy-agent@^8.0.2, socks-proxy-agent@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c" + integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== + dependencies: + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.8.3" + socks@^2.6.2: version "2.7.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" @@ -8427,6 +8580,14 @@ socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" +socks@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== + dependencies: + ip-address "^9.0.5" + smart-buffer "^4.2.0" + sonic-boom@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.0.1.tgz#515b7cef2c9290cb362c4536388ddeece07aed30" @@ -8449,7 +8610,7 @@ source-map-support@^0.5.21: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1: +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -8512,6 +8673,11 @@ split2@^4.0.0: resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -8543,16 +8709,7 @@ ssri@^9.0.0: dependencies: minipass "^3.1.1" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -8620,14 +8777,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -8888,11 +9038,16 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.11.1, tslib@^1.9.0: +tslib@^1.11.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.0.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.5.0, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" @@ -9340,7 +9495,7 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -9358,15 +9513,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -9523,7 +9669,7 @@ yarn-deduplicate@^6.0.2: semver "^7.5.0" tslib "^2.5.0" -yeoman-environment@^3.15.1, yeoman-environment@^3.19.3: +yeoman-environment@^3.15.1: version "3.19.3" resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-3.19.3.tgz#49c2339805fdf695fac42c88334a1daa94ee8b6c" integrity sha512-/+ODrTUHtlDPRH9qIC0JREH8+7nsRcjDl3Bxn2Xo/rvAaVvixH5275jHwg0C85g4QsF4P6M2ojfScPPAl+pLAg== @@ -9566,7 +9712,7 @@ yeoman-environment@^3.15.1, yeoman-environment@^3.19.3: textextensions "^5.12.0" untildify "^4.0.0" -yeoman-generator@^5.10.0, yeoman-generator@^5.8.0: +yeoman-generator@^5.8.0: version "5.10.0" resolved "https://registry.yarnpkg.com/yeoman-generator/-/yeoman-generator-5.10.0.tgz#0dde5be9d815b01f77a7e77ee6f9047edcbeca04" integrity sha512-iDUKykV7L4nDNzeYSedRmSeJ5eMYFucnKDi6KN1WNASXErgPepKqsQw55TgXPHnmpcyOh2Dd/LAZkyc+f0qaAw== From ebe987f96d2678d0dba430343de06d932cf0014a Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 26 Jul 2024 15:29:37 -0600 Subject: [PATCH 4/6] chore: update snapshot --- command-snapshot.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command-snapshot.json b/command-snapshot.json index ea8eba2c..343aacd5 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -60,7 +60,7 @@ "command": "dev:generate:library", "flagAliases": [], "flagChars": [], - "flags": ["flags-dir"], + "flags": ["dry-run", "flags-dir"], "plugin": "@salesforce/plugin-dev" }, { @@ -68,7 +68,7 @@ "command": "dev:generate:plugin", "flagAliases": [], "flagChars": [], - "flags": ["flags-dir"], + "flags": ["dry-run", "flags-dir"], "plugin": "@salesforce/plugin-dev" } ] From 541ed1be6e2b8f1db960e392cc38cd79a07944e0 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 26 Jul 2024 15:50:41 -0600 Subject: [PATCH 5/6] fix: use sf for finding existing commands --- src/commands/dev/generate/command.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/commands/dev/generate/command.ts b/src/commands/dev/generate/command.ts index bb844fc4..e3079a56 100644 --- a/src/commands/dev/generate/command.ts +++ b/src/commands/dev/generate/command.ts @@ -12,6 +12,7 @@ import defaultsDeep from 'lodash.defaultsdeep'; import { pascalCase } from 'change-case'; import { set } from '@salesforce/kit'; import { get } from '@salesforce/ts-types'; +import shelljs from 'shelljs'; import { Generator } from '../../../generator.js'; import { Topic } from '../../../types.js'; @@ -106,7 +107,10 @@ export default class GenerateCommand extends SfCommand { if (Object.keys(generator.pjson.devDependencies).includes('@salesforce/plugin-command-reference')) { // Get a list of all commands in `sf`. We will use this to determine if a topic is internal or external. - const commands = this.config.commandIDs.map((command) => command.replace(/:/g, '.').replace(/ /g, '.')); + const sfCommandsStdout = shelljs.exec('sf commands --json', { silent: true }).stdout; + const commandsJson = JSON.parse(sfCommandsStdout) as Array<{ id: string }>; + const commands = commandsJson.map((command) => command.id.replace(/:/g, '.').replace(/ /g, '.')); + const newTopics = addTopics(flags.name, generator.pjson.oclif.topics, commands); defaultsDeep(generator.pjson.oclif.topics, newTopics); } else { From c0b444048f9ef55088adccb9aea108f83abdacf8 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Mon, 29 Jul 2024 09:47:35 -0600 Subject: [PATCH 6/6] fix: ensure cwd is normalized --- src/generator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator.ts b/src/generator.ts index d936917c..bc24b4fd 100644 --- a/src/generator.ts +++ b/src/generator.ts @@ -6,7 +6,7 @@ */ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; -import { dirname, join, relative } from 'node:path'; +import { dirname, join, normalize, relative } from 'node:path'; import { fileURLToPath } from 'node:url'; import { renderFile } from 'ejs'; import { Ux } from '@salesforce/sf-plugins-core'; @@ -39,7 +39,7 @@ export class Generator { } public set cwd(value: string) { - this.workingDir = value; + this.workingDir = normalize(value); } public async render(source: string, destination: string, data?: Record): Promise {