diff --git a/docs/docs/usage/configuration_files.md b/docs/docs/usage/configuration_files.md index 234893e..c3e56fa 100644 --- a/docs/docs/usage/configuration_files.md +++ b/docs/docs/usage/configuration_files.md @@ -31,14 +31,17 @@ interface ScaffoldConfig { name: string templates: string[] output: FileResponse - createSubFolder?: boolean + subdir?: boolean + git?: string + config?: string + key?: string data?: Record overwrite?: FileResponse quiet?: boolean verbose?: LogLevel dryRun?: boolean helpers?: Record - subFolderNameHelper?: DefaultHelpers | string + subdirHelper?: DefaultHelpers | string beforeWrite?( content: Buffer, rawContent: Buffer, diff --git a/docs/docs/usage/examples.md b/docs/docs/usage/examples.md index 064c763..8538caa 100644 --- a/docs/docs/usage/examples.md +++ b/docs/docs/usage/examples.md @@ -32,19 +32,19 @@ title: Examples - Output file path: - - With `createSubFolder = false` (default): + - With `subdir = false` (default): ```text project → src → components → MyComponent.js ``` - - With `createSubFolder = true`: + - With `subdir = true`: ```text project → src → components → MyComponent → MyComponent.js ``` - - With `createSubFolder = true` and `subFolderNameHelper = 'upperCase'`: + - With `subdir = true` and `subdirHelper = 'upperCase'`: ```text project → src → components → MYCOMPONENT → MyComponent.js diff --git a/docs/docs/usage/migration.md b/docs/docs/usage/migration.md index 1d75cd1..9d8925c 100644 --- a/docs/docs/usage/migration.md +++ b/docs/docs/usage/migration.md @@ -6,9 +6,9 @@ title: Migration ### CLI option changes -- The `:template_key` syntax has been removed. You can still use `-k template_key` to achieve the - same result. - Several changes to how remote configs are loaded via CLI: + - The `:template_key` syntax has been removed. You can still use `-k template_key` to achieve the + same result. - The `--github` (`-gh`) flag has been replaced by a generic `--git` (`-g`) one, which handles any git URL. Providing a partial GitHub path will default to trying to find the project on GitHub, e.g. `-g username/project` @@ -17,6 +17,10 @@ title: Migration which can find the file for you if it is in one of the supported filenames. - `verbose` can now take the names `debug`, `info`, `warn`, `error` or `none` (case insensitive) or as usual by using the numbering from before. +- `--create-sub-folder` (`-s`) has been renamed to `--subdir` (`-s`) in the CLI. The Node.js names + have been changed as well. +- `--sub-folder-name-helper` (`-sh`) has been renamed to `--subdir-helper` (`-sh`). The Node.js + names have been changed as well. - All boolean flags no longer take a value. `-q` instead of `-q 1` or `-q true`, `-s` instead of `-s 1`, `-w` instead of `-w 1`, etc. diff --git a/docs/docs/usage/templates.md b/docs/docs/usage/templates.md index b9ae49e..5f1c577 100644 --- a/docs/docs/usage/templates.md +++ b/docs/docs/usage/templates.md @@ -125,7 +125,7 @@ config.helpers = { ``` All of the above helpers (built in and custom) will also be available to you when using -`subFolderNameHelper` (`--sub-folder-name-helper`/`-sh`) as a possible value. +`subdirHelper` (`--sub-dir-helper`/`-H`) as a possible value. > To see more information on how helpers work and more features, see > [Handlebars.js docs](https://handlebarsjs.com/guide/#custom-helpers). diff --git a/package.json b/package.json index 4c71d34..1901c19 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,9 @@ "clean": "rm -rf dist/", "build": "pnpm clean && tsc && chmod -R +x ./dist && cp ./package.json ./README.md ./dist/", "dev": "tsc --watch", - "start": "node dist/scaffold.js", + "start": "ts-node src/scaffold.ts", "test": "jest", - "cmd": "node --trace-warnings dist/cmd.js", - "build-test": "pnpm build && pnpm test", - "build-cmd": "pnpm build && pnpm cmd", + "cmd": "ts-node src/cmd.ts", "docs:build": "cd docs && pnpm build", "docs:watch": "cd docs && pnpm start", "audit-fix": "pnpm audit --fix", @@ -39,7 +37,7 @@ "date-fns": "^3.3.1", "glob": "^10.3.10", "handlebars": "^4.7.8", - "massarg": "2.0.0-pre.11" + "massarg": "2.0.0-pre.12" }, "devDependencies": { "@semantic-release/changelog": "^6.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b56803..23ea124 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,8 +18,8 @@ dependencies: specifier: ^4.7.8 version: 4.7.8 massarg: - specifier: 2.0.0-pre.11 - version: 2.0.0-pre.11 + specifier: 2.0.0-pre.12 + version: 2.0.0-pre.12 devDependencies: '@semantic-release/changelog': @@ -3164,8 +3164,8 @@ packages: hasBin: true dev: true - /massarg@2.0.0-pre.11: - resolution: {integrity: sha512-MrN5ZllZyGI8DPSA8o164WgeEhfpDYnFvtA0xRoWrGJ2eCDUeyyCItUk3EB55tV3kwysqi6AAOei49xUQ5BG4w==} + /massarg@2.0.0-pre.12: + resolution: {integrity: sha512-ncD9mXRQh4y8IIi5qcNEzh0qsqXyVq0FH0/v2vdMy/+WJCdAIuKG3TqlWML1SWMH4QdqZ/XJnbDfp8u2i91NNg==} dependencies: zod: 3.22.4 dev: false diff --git a/src/cmd.ts b/src/cmd.ts index c87b9a2..29bf2c0 100644 --- a/src/cmd.ts +++ b/src/cmd.ts @@ -8,7 +8,8 @@ import fs from "node:fs/promises" import { parseAppendData, parseConfigFile } from "./config" export async function parseCliArgs(args = process.argv.slice(2)) { - const pkgFile = await fs.readFile(path.join(__dirname, "package.json")) + const isProjectRoot = Boolean(await fs.stat(path.join(__dirname, "package.json")).catch(() => false)) + const pkgFile = await fs.readFile(path.resolve(__dirname, isProjectRoot ? "." : "..", "package.json")) const pkg = JSON.parse(pkgFile.toString()) const isConfigProvided = args.includes("--config") || args.includes("-c") || args.includes("--git") || args.includes("-g") @@ -56,7 +57,7 @@ export async function parseCliArgs(args = process.argv.slice(2)) { name: "output", aliases: ["o"], description: - "Path to output to. If `--create-sub-folder` is enabled, the subfolder will be created inside " + + "Path to output to. If `--subdir` is enabled, the subfolder will be created inside " + "this path. Default is current working directory.", required: !isConfigProvided, }) @@ -75,6 +76,7 @@ export async function parseCliArgs(args = process.argv.slice(2)) { aliases: ["w"], defaultValue: false, description: "Enable to override output files, even if they already exist.", + negatable: true, }) .option({ name: "data", @@ -91,15 +93,17 @@ export async function parseCliArgs(args = process.argv.slice(2)) { parse: parseAppendData, }) .flag({ - name: "create-sub-folder", + name: "subdir", aliases: ["s"], defaultValue: false, - description: "Create subfolder with the input name", + description: "Create a parent directory with the input name (and possibly `--subdir-helper`", + negatable: true, + negationName: "no-subdir", }) .option({ - name: "sub-folder-name-helper", - aliases: ["sh"], - description: "Default helper to apply to subfolder name when using `--create-sub-folder true`.", + name: "subdir-helper", + aliases: ["H"], + description: "Default helper to apply to subfolder name when using `--subdir`.", }) .flag({ name: "quiet", @@ -150,16 +154,17 @@ export async function parseCliArgs(args = process.argv.slice(2)) { bindOption: true, lineLength: 100, useGlobalTableColumns: true, - // optionOptions: { - // displayNegations: true, - // }, + usageText: [chalk.yellow`simple-scaffold`, chalk.gray`[options]`, chalk.cyan``].join(" "), + optionOptions: { + displayNegations: true, + }, footerText: [ `Version: ${pkg.version}`, `Copyright © Chen Asraf 2017-${new Date().getFullYear()}`, ``, - `Documentation:\n ${chalk.underline`https://chenasraf.github.io/simple-scaffold`}`, - `NPM:\n ${chalk.underline`https://npmjs.com/package/simple-scaffold`}`, - `GitHub:\n ${chalk.underline`https://github.com/chenasraf/simple-scaffold`}`, + `Documentation: ${chalk.underline`https://chenasraf.github.io/simple-scaffold`}`, + `NPM: ${chalk.underline`https://npmjs.com/package/simple-scaffold`}`, + `GitHub: ${chalk.underline`https://github.com/chenasraf/simple-scaffold`}`, ].join("\n"), }) .parse(args) diff --git a/src/file.ts b/src/file.ts index e4f91d8..0048139 100644 --- a/src/file.ts +++ b/src/file.ts @@ -165,9 +165,9 @@ export function getOutputDir(config: ScaffoldConfig, outputPathOpt: string, base ...([ outputPathOpt, basePath, - config.createSubFolder - ? config.subFolderNameHelper - ? handlebarsParse(config, `{{ ${config.subFolderNameHelper} name }}`).toString() + config.subdir + ? config.subdirHelper + ? handlebarsParse(config, `{{ ${config.subdirHelper} name }}`).toString() : config.name : undefined, ].filter(Boolean) as string[]), diff --git a/src/logger.ts b/src/logger.ts index 14d841f..7ebd8df 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -2,7 +2,15 @@ import { LogConfig, LogLevel, ScaffoldConfig } from "./types" import chalk from "chalk" export function log(config: LogConfig, level: LogLevel, ...obj: any[]): void { - if (config.logLevel === LogLevel.none || level < (config.logLevel ?? LogLevel.info)) { + const priority: Record = { + [LogLevel.none]: 0, + [LogLevel.debug]: 1, + [LogLevel.info]: 2, + [LogLevel.warning]: 3, + [LogLevel.error]: 4, + } + + if (config.logLevel === LogLevel.none || priority[level] < priority[config.logLevel ?? LogLevel.info]) { return } @@ -49,10 +57,10 @@ export function logInitStep(config: ScaffoldConfig): void { name: config.name, templates: config.templates, output: config.output, - createSubFolder: config.createSubFolder, + subdir: config.subdir, data: config.data, overwrite: config.overwrite, - subFolderNameHelper: config.subFolderNameHelper, + subdirHelper: config.subdirHelper, helpers: Object.keys(config.helpers ?? {}), logLevel: config.logLevel, dryRun: config.dryRun, diff --git a/src/scaffold.ts b/src/scaffold.ts index cbe0034..baee730 100644 --- a/src/scaffold.ts +++ b/src/scaffold.ts @@ -123,7 +123,7 @@ Scaffold.fromConfig = async function ( logLevel: LogLevel.info, overwrite: false, templates: [], - createSubFolder: false, + subdir: false, quiet: false, config: pathOrUrl, ...config, diff --git a/src/types.ts b/src/types.ts index a303860..9a751a1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -27,7 +27,7 @@ export interface ScaffoldConfig { templates: string[] /** - * Path to output to. If `createSubFolder` is `true`, the subfolder will be created inside this path. + * Path to output to. If `subdir` is `true`, the subfolder will be created inside this path. * * May also be a {@link FileResponseHandler} which returns a new output path to override the default one. * @@ -39,12 +39,12 @@ export interface ScaffoldConfig { /** * Whether to create subfolder with the input name. * - * When `true`, you may also use {@link subFolderNameHelper} to determine a pre-process helper on + * When `true`, you may also use {@link subdirHelper} to determine a pre-process helper on * the directory name. * * @default `false` */ - createSubFolder?: boolean + subdir?: boolean /** * Add custom data to the templates. By default, only your app name is included as `{{name}}` and `{{Name}}`. @@ -131,15 +131,15 @@ export interface ScaffoldConfig { helpers?: Record /** - * Default transformer to apply to subfolder name when using `createSubFolder: true`. Can be one of the default + * Default transformer to apply to subfolder name when using `subdir: true`. Can be one of the default * capitalization helpers, or a custom one you provide to `helpers`. Defaults to `undefined`, which means no * transformation is done. * - * @see {@link createSubFolder} + * @see {@link subdir} * @see {@link CaseHelpers} * @see {@link DefaultHelpers} */ - subFolderNameHelper?: DefaultHelpers | string + subdirHelper?: DefaultHelpers | string /** * This callback runs right before content is being written to the disk. If you supply this function, you may return @@ -182,7 +182,7 @@ export interface ScaffoldConfig { * @see {@link DefaultHelpers} * @see {@link DateHelpers} * @see {@link ScaffoldConfig} - * @see {@link ScaffoldConfig.subFolderNameHelper} + * @see {@link ScaffoldConfig.subdirHelper} * * @category Helpers */ @@ -320,20 +320,42 @@ export type FileResponseHandler = (fullPath: string, basedir: string, basenam * */ export type FileResponse = T | FileResponseHandler -/** @internal */ +/** + * The Scaffold config for CLI + * Contains less and more specific options than {@link ScaffoldConfig} + */ export interface ScaffoldCmdConfig { + /** The name of the scaffold template to use. */ name: string + /** The templates to use for generation */ templates: string[] + /** The output path to write to */ output: string - createSubFolder: boolean + /** Whether to create subfolder with the input name */ + subdir: boolean + /** Default transformer to apply to subfolder name when using `subdir: true` */ + subdirHelper?: string + /** Add custom data to the templates */ data?: Record + /** Add custom data to the template in a CLI-friendly syntax (and not JSON) */ appendData?: Record + /** Enable to override output files, even if they already exist */ overwrite: boolean + /** Silence logs, same as `logLevel: "none"` */ quiet: boolean + /** + * Determine amount of logs to display. + * + * @see {@link LogLevel} + */ logLevel: LogLevel + /** Don't emit files. This is good for testing your scaffolds and making sure they don't fail, without having to write actual file contents or create directories. */ dryRun: boolean + /** Config file path to use */ config?: string + /** The key of the template to use */ key?: string + /** The git repository to use to fetch the config file */ git?: string } diff --git a/tests/scaffold.test.ts b/tests/scaffold.test.ts index 469016d..b4f850f 100644 --- a/tests/scaffold.test.ts +++ b/tests/scaffold.test.ts @@ -110,7 +110,7 @@ describe("Scaffold", () => { name: "app_name", output: "output", templates: ["input"], - createSubFolder: true, + subdir: true, logLevel: "none", }) @@ -402,7 +402,7 @@ describe("Scaffold", () => { name: "app_name", output: "output", templates: ["input"], - createSubFolder: true, + subdir: true, logLevel: "none", }) @@ -415,9 +415,9 @@ describe("Scaffold", () => { name: "app_name", output: "output", templates: ["input"], - createSubFolder: true, + subdir: true, logLevel: "none", - subFolderNameHelper: "upperCase", + subdirHelper: "upperCase", }) const data = readFileSync(join(process.cwd(), "output", "APP_NAME", "app_name.txt")) @@ -429,9 +429,9 @@ describe("Scaffold", () => { name: "app_name", output: "output", templates: ["input"], - createSubFolder: true, + subdir: true, logLevel: "none", - subFolderNameHelper: "test", + subdirHelper: "test", helpers: { test: () => "REPLACED", },