-
Notifications
You must be signed in to change notification settings - Fork 16
feat(cli): split cli into sub commands #250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+648
−171
Merged
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
f2ebf8b
feat(cli): split cli into sub commands
avivkeller 0255fb6
split into multiple files
avivkeller d480352
add jsdoc
avivkeller fbb3009
include boolean flags in final exec
avivkeller 91d26b2
suggestion from review
avivkeller 4d1a9de
code review
avivkeller 3fad262
Merge branch 'feat/cli/subcommands' of https://github.com/nodejs/api-…
avivkeller 780f9e4
show basic help
avivkeller File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,161 +1,62 @@ | ||
#!/usr/bin/env node | ||
|
||
import { resolve } from 'node:path'; | ||
import process from 'node:process'; | ||
import { cpus } from 'node:os'; | ||
import { Argument, Command, Option } from 'commander'; | ||
|
||
import { Command, Option } from 'commander'; | ||
import interactive from './commands/interactive.mjs'; | ||
import list, { types } from './commands/list.mjs'; | ||
import commands from './commands/index.mjs'; | ||
|
||
import { coerce } from 'semver'; | ||
import { DOC_NODE_CHANGELOG_URL, DOC_NODE_VERSION } from '../src/constants.mjs'; | ||
import createGenerator from '../src/generators.mjs'; | ||
import { publicGenerators } from '../src/generators/index.mjs'; | ||
import createLinter from '../src/linter/index.mjs'; | ||
import reporters from '../src/linter/reporters/index.mjs'; | ||
import rules from '../src/linter/rules/index.mjs'; | ||
import createMarkdownLoader from '../src/loaders/markdown.mjs'; | ||
import createMarkdownParser from '../src/parsers/markdown.mjs'; | ||
import createNodeReleases from '../src/releases.mjs'; | ||
|
||
const availableGenerators = Object.keys(publicGenerators); | ||
|
||
const program = new Command(); | ||
|
||
program | ||
const program = new Command() | ||
.name('api-docs-tooling') | ||
.description('CLI tool to generate API documentation of a Node.js project.') | ||
.addOption( | ||
new Option( | ||
'-i, --input [patterns...]', | ||
'Specify input file patterns using glob syntax' | ||
).makeOptionMandatory() | ||
) | ||
.addOption( | ||
new Option( | ||
'--ignore [patterns...]', | ||
'Specify which input files to ignore using glob syntax' | ||
) | ||
) | ||
.addOption( | ||
new Option( | ||
'-o, --output <path>', | ||
'Specify the relative or absolute output directory' | ||
) | ||
) | ||
.addOption( | ||
new Option( | ||
'-v, --version <semver>', | ||
'Specify the target version of Node.js, semver compliant' | ||
).default(DOC_NODE_VERSION) | ||
) | ||
.addOption( | ||
new Option( | ||
'-c, --changelog <url>', | ||
'Specify the path (file: or https://) to the CHANGELOG.md file' | ||
).default(DOC_NODE_CHANGELOG_URL) | ||
) | ||
.addOption( | ||
new Option( | ||
'-t, --target [mode...]', | ||
'Set the processing target modes' | ||
).choices(availableGenerators) | ||
) | ||
.addOption( | ||
new Option('--disable-rule [rule...]', 'Disable a specific linter rule') | ||
.choices(Object.keys(rules)) | ||
.default([]) | ||
) | ||
.addOption( | ||
new Option('--lint-dry-run', 'Run linter in dry-run mode').default(false) | ||
) | ||
.addOption( | ||
new Option('--git-ref', 'A git ref/commit URL pointing to Node.js').default( | ||
'https://github.com/nodejs/node/tree/HEAD' | ||
) | ||
) | ||
.addOption( | ||
new Option('-r, --reporter [reporter]', 'Specify the linter reporter') | ||
.choices(Object.keys(reporters)) | ||
.default('console') | ||
) | ||
.addOption( | ||
new Option( | ||
'-p, --threads <number>', | ||
'The maximum number of threads to use. Set to 1 to disable parallelism' | ||
).default(Math.max(1, cpus().length - 1)) | ||
) | ||
.parse(process.argv); | ||
.description('CLI tool to generate and lint Node.js API documentation'); | ||
|
||
/** | ||
* @typedef {keyof publicGenerators} Target A list of the available generator names. | ||
* | ||
* @typedef {Object} Options | ||
* @property {Array<string>|string} input Specifies the glob/path for input files. | ||
* @property {string} output Specifies the directory where output files will be saved. | ||
* @property {Target[]} target Specifies the generator target mode. | ||
* @property {string} version Specifies the target Node.js version. | ||
* @property {string} changelog Specifies the path to the Node.js CHANGELOG.md file. | ||
* @property {string[]} disableRule Specifies the linter rules to disable. | ||
* @property {boolean} lintDryRun Specifies whether the linter should run in dry-run mode. | ||
* @property {boolean} useGit Specifies whether the parser should execute optional git commands. (Should only be used within a git repo) | ||
* @property {keyof reporters} reporter Specifies the linter reporter. | ||
* | ||
* @name ProgramOptions | ||
* @type {Options} | ||
* @description The return type for values sent to the program from the CLI. | ||
*/ | ||
const { | ||
input, | ||
ignore, | ||
output, | ||
target = [], | ||
version, | ||
changelog, | ||
disableRule, | ||
lintDryRun, | ||
gitRef, | ||
reporter, | ||
threads, | ||
} = program.opts(); | ||
// Registering generate and lint commands | ||
commands.forEach(({ name, description, options, action }) => { | ||
const cmd = program.command(name).description(description); | ||
|
||
const linter = createLinter(lintDryRun, disableRule); | ||
// Add options to the command | ||
Object.values(options).forEach(({ flags, desc, prompt }) => { | ||
const option = new Option(flags.join(', '), desc).default( | ||
prompt.initialValue | ||
); | ||
|
||
const { loadFiles } = createMarkdownLoader(); | ||
const { parseApiDocs } = createMarkdownParser(); | ||
if (prompt.required) { | ||
option.makeOptionMandatory(); | ||
} | ||
|
||
const apiDocFiles = await loadFiles(input, ignore); | ||
if (prompt.type === 'multiselect') { | ||
option.choices(prompt.options.map(({ value }) => value)); | ||
} | ||
|
||
const parsedApiDocs = await parseApiDocs(apiDocFiles); | ||
cmd.addOption(option); | ||
}); | ||
|
||
const { runGenerators } = createGenerator(parsedApiDocs); | ||
// Set the action for the command | ||
cmd.action(action); | ||
}); | ||
|
||
// Retrieves Node.js release metadata from a given Node.js version and CHANGELOG.md file | ||
const { getAllMajors } = createNodeReleases(changelog); | ||
// Register the interactive command | ||
program | ||
.command('interactive') | ||
.description('Launch guided CLI wizard') | ||
.action(interactive); | ||
|
||
// Runs the Linter on the parsed API docs | ||
linter.lintAll(parsedApiDocs); | ||
// Register the list command | ||
program | ||
.command('list') | ||
.addArgument(new Argument('<types>', 'The type to list').choices(types)) | ||
.description('List the given type') | ||
.action(list); | ||
|
||
if (target) { | ||
await runGenerators({ | ||
// A list of target modes for the API docs parser | ||
generators: target, | ||
// Resolved `input` to be used | ||
input: input, | ||
// Resolved `output` path to be used | ||
output: output && resolve(output), | ||
// Resolved SemVer of current Node.js version | ||
version: coerce(version), | ||
// A list of all Node.js major versions with LTS status | ||
releases: await getAllMajors(), | ||
// An URL containing a git ref URL pointing to the commit or ref that was used | ||
// to generate the API docs. This is used to link to the source code of the | ||
gitRef, | ||
// How many threads should be used | ||
threads, | ||
// Register the help command | ||
program | ||
.command('help [cmd]') | ||
.description('Show help for a command') | ||
.action(cmdName => { | ||
const target = program.commands.find(c => c.name() === cmdName) ?? program; | ||
avivkeller marked this conversation as resolved.
Show resolved
Hide resolved
|
||
target.help(); | ||
}); | ||
} | ||
|
||
// Reports Lint Content | ||
linter.report(reporter); | ||
|
||
process.exitCode = Number(linter.hasError()); | ||
// Parse and execute command-line arguments | ||
program.parse(process.argv); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import { cpus } from 'node:os'; | ||
import { resolve } from 'node:path'; | ||
import process from 'node:process'; | ||
|
||
import { coerce } from 'semver'; | ||
|
||
import { | ||
DOC_NODE_CHANGELOG_URL, | ||
DOC_NODE_VERSION, | ||
} from '../../src/constants.mjs'; | ||
import createGenerator from '../../src/generators.mjs'; | ||
import { publicGenerators } from '../../src/generators/index.mjs'; | ||
import createNodeReleases from '../../src/releases.mjs'; | ||
import { loadAndParse } from '../utils.mjs'; | ||
import { runLint } from './lint.mjs'; | ||
|
||
const availableGenerators = Object.keys(publicGenerators); | ||
|
||
/** | ||
* @typedef {Object} Options | ||
* @property {Array<string>|string} input - Specifies the glob/path for input files. | ||
* @property {Array<string>|string} [ignore] - Specifies the glob/path for ignoring files. | ||
* @property {Array<keyof publicGenerators>} target - Specifies the generator target mode. | ||
* @property {string} version - Specifies the target Node.js version. | ||
* @property {string} changelog - Specifies the path to the Node.js CHANGELOG.md file. | ||
* @property {string} [gitRef] - Git ref/commit URL. | ||
* @property {number} [threads] - Number of threads to allow. | ||
* @property {boolean} [skipLint] - Skip lint before generate. | ||
*/ | ||
|
||
/** | ||
* @type {import('../utils.mjs').Command} | ||
*/ | ||
export default { | ||
description: 'Generate API docs', | ||
name: 'generate', | ||
options: { | ||
input: { | ||
flags: ['-i', '--input <patterns...>'], | ||
desc: 'Input file patterns (glob)', | ||
prompt: { | ||
type: 'text', | ||
message: 'Enter input glob patterns', | ||
variadic: true, | ||
required: true, | ||
}, | ||
}, | ||
ignore: { | ||
flags: ['--ignore [patterns...]'], | ||
desc: 'Ignore patterns (comma-separated)', | ||
prompt: { | ||
type: 'text', | ||
message: 'Enter ignore patterns', | ||
variadic: true, | ||
}, | ||
}, | ||
output: { | ||
flags: ['-o', '--output <dir>'], | ||
desc: 'Output directory', | ||
prompt: { type: 'text', message: 'Enter output directory' }, | ||
}, | ||
threads: { | ||
flags: ['-p', '--threads <number>'], | ||
prompt: { | ||
type: 'text', | ||
message: 'How many threads to allow', | ||
initialValue: String(Math.max(cpus().length, 1)), | ||
}, | ||
}, | ||
version: { | ||
flags: ['-v', '--version <semver>'], | ||
desc: 'Target Node.js version', | ||
prompt: { | ||
type: 'text', | ||
message: 'Enter Node.js version', | ||
initialValue: DOC_NODE_VERSION, | ||
}, | ||
}, | ||
changelog: { | ||
flags: ['-c', '--changelog <url>'], | ||
desc: 'Changelog URL or path', | ||
prompt: { | ||
type: 'text', | ||
message: 'Enter changelog URL', | ||
initialValue: DOC_NODE_CHANGELOG_URL, | ||
}, | ||
}, | ||
gitRef: { | ||
flags: ['--git-ref <url>'], | ||
desc: 'Git ref/commit URL', | ||
prompt: { | ||
type: 'text', | ||
message: 'Enter Git ref URL', | ||
initialValue: 'https://github.com/nodejs/node/tree/HEAD', | ||
}, | ||
}, | ||
target: { | ||
flags: ['-t', '--target [modes...]'], | ||
desc: 'Target generator modes', | ||
prompt: { | ||
required: true, | ||
type: 'multiselect', | ||
message: 'Choose target generators', | ||
options: availableGenerators.map(g => ({ | ||
value: g, | ||
label: `${publicGenerators[g].name || g} (v${publicGenerators[g].version}) - ${publicGenerators[g].description}`, | ||
})), | ||
}, | ||
}, | ||
skipLint: { | ||
flags: ['--no-lint'], | ||
desc: 'Skip lint before generate', | ||
prompt: { | ||
type: 'confirm', | ||
message: 'Skip lint before generate?', | ||
initialValue: false, | ||
}, | ||
}, | ||
}, | ||
/** | ||
* Handles the action for generating API docs | ||
* @param {Options} opts - The options to generate API docs. | ||
* @returns {Promise<void>} | ||
*/ | ||
async action(opts) { | ||
const docs = await loadAndParse(opts.input, opts.ignore); | ||
|
||
if (!opts.skipLint && !runLint(docs)) { | ||
console.error('Lint failed; aborting generation.'); | ||
process.exit(1); | ||
} | ||
|
||
const { runGenerators } = createGenerator(docs); | ||
const { getAllMajors } = createNodeReleases(opts.changelog); | ||
|
||
await runGenerators({ | ||
generators: opts.target, | ||
input: opts.input, | ||
output: opts.output && resolve(opts.output), | ||
version: coerce(opts.version), | ||
releases: await getAllMajors(), | ||
gitRef: opts.gitRef, | ||
threads: parseInt(opts.threads, 10), | ||
}); | ||
}, | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import generate from './generate.mjs'; | ||
import lint from './lint.mjs'; | ||
|
||
export default [generate, lint]; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import { spawnSync } from 'node:child_process'; | ||
import process from 'node:process'; | ||
|
||
import { | ||
intro, | ||
outro, | ||
select, | ||
multiselect, | ||
text, | ||
confirm, | ||
isCancel, | ||
cancel, | ||
} from '@clack/prompts'; | ||
|
||
import commands from './index.mjs'; | ||
|
||
/** | ||
* Validates that a string is not empty. | ||
* @param {string} value The input string to validate. | ||
* @returns {string|undefined} A validation message or undefined if valid. | ||
*/ | ||
function requireValue(value) { | ||
if (value.length === 0) { | ||
return 'Value is required!'; | ||
} | ||
} | ||
|
||
/** | ||
* Retrieves the prompt message based on whether the field is required or has an initial value. | ||
* @param {Object} prompt The prompt definition. | ||
* @param {string} prompt.message The message to display. | ||
* @param {boolean} prompt.required Whether the input is required. | ||
* @param {string} [prompt.initialValue] The initial value of the input field. | ||
* @returns {string} The message to display in the prompt. | ||
*/ | ||
function getMessage({ message, required, initialValue }) { | ||
return required || initialValue ? message : `${message} (Optional)`; | ||
} | ||
|
||
/** | ||
* Escapes shell argument to ensure it's safe for inclusion in shell commands. | ||
* @param {string} arg The argument to escape. | ||
* @returns {string} The escaped argument. | ||
*/ | ||
function escapeShellArg(arg) { | ||
// Return the argument as is if it's alphanumeric or contains safe characters | ||
if (/^[a-zA-Z0-9_/-]+$/.test(arg)) { | ||
return arg; | ||
} | ||
// Escape single quotes in the argument | ||
return `'${arg.replace(/'/g, `'\\''`)}'`; | ||
} | ||
|
||
/** | ||
* Main interactive function for the API Docs Tooling command line interface. | ||
* Guides the user through a series of prompts, validates inputs, and generates a command to run. | ||
* @returns {Promise<void>} Resolves once the command is generated and executed. | ||
*/ | ||
export default async function interactive() { | ||
// Step 1: Introduction to the tool | ||
intro('Welcome to API Docs Tooling'); | ||
|
||
// Step 2: Choose the action based on available command definitions | ||
const actionOptions = commands.map(({ description }, i) => ({ | ||
label: description, | ||
value: i, | ||
})); | ||
|
||
const selectedAction = await select({ | ||
message: 'What would you like to do?', | ||
options: actionOptions, | ||
}); | ||
|
||
if (isCancel(selectedAction)) { | ||
cancel('Cancelled.'); | ||
process.exit(0); | ||
} | ||
|
||
// Retrieve the options for the selected action | ||
const { options, name } = commands[selectedAction]; | ||
const answers = {}; // Store answers from user prompts | ||
|
||
// Step 3: Collect input for each option | ||
for (const [key, { prompt }] of Object.entries(options)) { | ||
let response; | ||
const promptMessage = getMessage(prompt); | ||
|
||
switch (prompt.type) { | ||
case 'text': | ||
response = await text({ | ||
message: promptMessage, | ||
initialValue: prompt.initialValue || '', | ||
validate: prompt.required ? requireValue : undefined, | ||
}); | ||
if (response) { | ||
// Store response; split into an array if variadic | ||
answers[key] = prompt.variadic | ||
? response.split(',').map(s => s.trim()) | ||
: response; | ||
} | ||
break; | ||
|
||
case 'confirm': | ||
response = await confirm({ | ||
message: promptMessage, | ||
initialValue: prompt.initialValue, | ||
}); | ||
answers[key] = response; | ||
break; | ||
|
||
case 'multiselect': | ||
response = await multiselect({ | ||
message: promptMessage, | ||
options: prompt.options, | ||
required: !!prompt.required, | ||
}); | ||
answers[key] = response; | ||
break; | ||
|
||
case 'select': | ||
response = await select({ | ||
message: promptMessage, | ||
options: prompt.options, | ||
}); | ||
answers[key] = response; | ||
break; | ||
} | ||
|
||
// Handle cancellation | ||
if (isCancel(response)) { | ||
cancel('Cancelled.'); | ||
process.exit(0); | ||
} | ||
} | ||
|
||
// Step 4: Build the final command by escaping values | ||
const cmdParts = ['npx', 'api-docs-tooling', name]; | ||
const executionArgs = [name]; | ||
|
||
for (const [key, { flags }] of Object.entries(options)) { | ||
const value = answers[key]; | ||
// Skip empty values | ||
if (value == null || (Array.isArray(value) && value.length === 0)) { | ||
continue; | ||
} | ||
|
||
const flag = flags[0].split(/[\s,]+/)[0]; // Use the first flag | ||
|
||
// Handle different value types (boolean, array, string) | ||
if (typeof value === 'boolean') { | ||
if (value) { | ||
cmdParts.push(flag); | ||
executionArgs.push(flag); | ||
} | ||
} else if (Array.isArray(value)) { | ||
for (const item of value) { | ||
cmdParts.push(flag, escapeShellArg(item)); | ||
executionArgs.push(flag, item); | ||
} | ||
} else { | ||
cmdParts.push(flag, escapeShellArg(value)); | ||
executionArgs.push(flag, value); | ||
} | ||
} | ||
|
||
const finalCommand = cmdParts.join(' '); | ||
|
||
console.log(`\nGenerated command:\n${finalCommand}\n`); | ||
|
||
// Step 5: Confirm and execute the generated command | ||
if (await confirm({ message: 'Run now?', initialValue: true })) { | ||
spawnSync(process.execPath, [process.argv[1], ...executionArgs], { | ||
stdio: 'inherit', | ||
}); | ||
} | ||
|
||
outro('Done!'); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import process from 'node:process'; | ||
|
||
import createLinter from '../../src/linter/index.mjs'; | ||
import reporters from '../../src/linter/reporters/index.mjs'; | ||
import rules from '../../src/linter/rules/index.mjs'; | ||
import { loadAndParse } from '../utils.mjs'; | ||
|
||
const availableRules = Object.keys(rules); | ||
const availableReporters = Object.keys(reporters); | ||
|
||
/** | ||
* @typedef {Object} LinterOptions | ||
* @property {Array<string>|string} input - Glob/path for input files. | ||
* @property {Array<string>|string} [ignore] - Glob/path for ignoring files. | ||
* @property {string[]} [disableRule] - Linter rules to disable. | ||
* @property {boolean} [dryRun] - Dry-run mode. | ||
* @property {keyof reporters} reporter - Reporter for linter output. | ||
*/ | ||
|
||
/** | ||
* Run the linter on parsed documentation. | ||
* @param {ApiDocMetadataEntry[]} docs - Parsed documentation objects. | ||
* @param {LinterOptions} options - Linter configuration options. | ||
* @returns {boolean} - True if no errors, false otherwise. | ||
*/ | ||
export function runLint( | ||
docs, | ||
{ disableRule = [], dryRun = false, reporter = 'console' } = {} | ||
) { | ||
const linter = createLinter(dryRun, disableRule); | ||
linter.lintAll(docs); | ||
linter.report(reporter); | ||
return !linter.hasError(); | ||
} | ||
|
||
/** | ||
* @type {import('../utils.mjs').Command} | ||
*/ | ||
export default { | ||
name: 'lint', | ||
description: 'Run linter independently', | ||
options: { | ||
input: { | ||
flags: ['-i', '--input <patterns...>'], | ||
desc: 'Input file patterns (glob)', | ||
prompt: { | ||
type: 'text', | ||
message: 'Enter input glob patterns', | ||
variadic: true, | ||
required: true, | ||
}, | ||
}, | ||
ignore: { | ||
flags: ['--ignore [patterns...]'], | ||
desc: 'Ignore patterns (comma-separated)', | ||
prompt: { | ||
type: 'text', | ||
message: 'Enter ignore patterns', | ||
variadic: true, | ||
}, | ||
}, | ||
disableRule: { | ||
flags: ['--disable-rule [rules...]'], | ||
desc: 'Disable linter rules', | ||
prompt: { | ||
type: 'multiselect', | ||
message: 'Choose rules to disable', | ||
options: availableRules.map(r => ({ label: r, value: r })), | ||
}, | ||
}, | ||
dryRun: { | ||
flags: ['--dry-run'], | ||
desc: 'Dry run mode', | ||
prompt: { | ||
type: 'confirm', | ||
message: 'Enable dry run mode?', | ||
initialValue: false, | ||
}, | ||
}, | ||
reporter: { | ||
flags: ['-r', '--reporter <reporter>'], | ||
desc: 'Linter reporter to use', | ||
prompt: { | ||
type: 'select', | ||
message: 'Choose a reporter', | ||
options: availableReporters.map(r => ({ label: r, value: r })), | ||
}, | ||
}, | ||
}, | ||
|
||
/** | ||
* Action for running the linter | ||
* @param {LinterOptions} opts - Linter options. | ||
* @returns {Promise<void>} | ||
*/ | ||
async action(opts) { | ||
try { | ||
const docs = await loadAndParse(opts.input, opts.ignore); | ||
const success = runLint(docs, opts); | ||
process.exitCode = success ? 0 : 1; | ||
} catch (error) { | ||
console.error('Error running the linter:', error); | ||
process.exitCode = 1; | ||
} | ||
}, | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { publicGenerators } from '../../src/generators/index.mjs'; | ||
import reporters from '../../src/linter/reporters/index.mjs'; | ||
import rules from '../../src/linter/rules/index.mjs'; | ||
|
||
const availableRules = Object.keys(rules); | ||
const availableReporters = Object.keys(reporters); | ||
|
||
export const types = ['generators', 'rules', 'reporters']; | ||
|
||
/** | ||
* Lists available generators, rules, or reporters based on the given type. | ||
* | ||
* @param {'generators' | 'rules' | 'reporters'} type - The type of items to list. | ||
*/ | ||
export default function list(type) { | ||
const list = | ||
type === 'generators' | ||
? Object.entries(publicGenerators).map( | ||
([key, generator]) => | ||
`${generator.name || key} (v${generator.version}) - ${generator.description}` | ||
) | ||
: type === 'rules' | ||
? availableRules | ||
: availableReporters; | ||
|
||
console.log(list.join('\n')); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import createMarkdownLoader from '../src/loaders/markdown.mjs'; | ||
import createMarkdownParser from '../src/parsers/markdown.mjs'; | ||
|
||
// Instantiate loader and parser once to reuse | ||
const loader = createMarkdownLoader(); | ||
const parser = createMarkdownParser(); | ||
|
||
/** | ||
* Load and parse markdown API docs. | ||
* @param {string[]} input - Glob patterns for input files. | ||
* @param {string[]} [ignore] - Glob patterns to ignore. | ||
* @returns {Promise<ApiDocMetadataEntry[]>} - Parsed documentation objects. | ||
*/ | ||
export async function loadAndParse(input, ignore) { | ||
const files = await loader.loadFiles(input, ignore); | ||
return parser.parseApiDocs(files); | ||
} | ||
|
||
/** | ||
* Represents a command-line option for the linter CLI. | ||
* @typedef {Object} Option | ||
* @property {string[]} flags - Command-line flags, e.g., ['-i, --input <patterns...>']. | ||
* @property {string} desc - Description of the option. | ||
* @property {Object} [prompt] - Optional prompt configuration. | ||
* @property {'text'|'confirm'|'select'|'multiselect'} prompt.type - Type of the prompt. | ||
* @property {string} prompt.message - Message displayed in the prompt. | ||
* @property {boolean} [prompt.variadic] - Indicates if the prompt accepts multiple values. | ||
* @property {boolean} [prompt.required] - Whether the prompt is required. | ||
* @property {boolean} [prompt.initialValue] - Default value for confirm prompts. | ||
* @property {{label: string, value: string}[]} [prompt.options] - Options for select/multiselect prompts. | ||
*/ | ||
|
||
/** | ||
* Represents a command-line subcommand | ||
* @typedef {Object} Command | ||
* @property {{ [key: string]: Option }} options | ||
* @property {string} name | ||
* @property {string} description | ||
* @property {Function} action | ||
*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
avivkeller marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.