From 9ce2845ace76938190dbfd5cd3cb090d23d0eebc Mon Sep 17 00:00:00 2001 From: Chen Asraf Date: Mon, 29 Jan 2024 01:37:40 +0200 Subject: [PATCH] feat!: separate git/github/config flags feat: separate git/github/config t test cleanup --- docs/docs/usage/cli.md | 35 +++++++++---------- package.json | 2 +- pnpm-lock.yaml | 8 ++--- src/cmd.ts | 12 +++++-- src/config.ts | 76 +++++++++++++++++++++++------------------- src/git.ts | 6 ++-- src/types.ts | 12 +++---- tests/config.test.ts | 50 +++------------------------ 8 files changed, 87 insertions(+), 114 deletions(-) diff --git a/docs/docs/usage/cli.md b/docs/docs/usage/cli.md index 399ff9a..d1e08f9 100644 --- a/docs/docs/usage/cli.md +++ b/docs/docs/usage/cli.md @@ -11,23 +11,24 @@ Usage: simple-scaffold [options] To see this and more information anytime, add the `-h` or `--help` flag to your call, e.g. `npx simple-scaffold@latest -h`. -| Command \| alias | | -| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `--name` \| `-n` | Name to be passed to the generated files. `{{name}}` and other data parameters inside contents and file names will be replaced accordingly. You may omit the `--name` or `-n` for this specific option. | -| `--config` \| `-c` | Filename or https git URL to load config from instead of passing arguments to CLI or using a Node.js script. See examples for syntax. | -| `--github` \| `-gh` | GitHub path to load config from instead of passing arguments to CLI or using a Node.js script. See examples for syntax. | -| `--key` \| `-k` | Key to load inside the config file. This overwrites the config key provided after the colon in `--config` (e.g. `--config scaffold.cmd.js:component`) | -| `--output` \| `-o` | Path to output to. If `--create-sub-folder` is enabled, the subfolder will be created inside this path. Default is current working directory. | -| `--templates` \| `-t` | Template files to use as input. You may provide multiple files, each of which can be a relative or absolute path, or a glob pattern for multiple file matching easily. | -| `--overwrite` \| `-w` | Enable to override output files, even if they already exist. | -| `--data` \| `-d` | Add custom data to the templates. By default, only your app name is included. | -| `--append-data` \| `-D` | Append additional custom data to the templates, which will overwrite `--data`, using an alternate syntax, which is easier to use with CLI: `-D key1=string -D key2:=raw` | -| `--create-sub-folder` \| `-s` | Create subfolder with the input name | -| `--sub-folder-name-helper` \| `-sh` | Default helper to apply to subfolder name when using `--create-sub-folder true`. | -| `--quiet` \| `-q` | Suppress output logs (Same as `--log-level none`) | -| `--log-level` \| `-l` | Determine amount of logs to display. The values are: `none \| debug \| info \| warn \| error`. The provided level will display messages of the same level or higher. | -| `--dry-run` \| `-dr` | 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. | -| `--help` \| `-h` | Show this help message | +| Command \| alias | | +| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--name` \| `-n` | Name to be passed to the generated files. `{{name}}` and other data parameters inside contents and file names will be replaced accordingly. You may omit the `--name` or `-n` for this specific option. | +| `--config`\|`-c` | Filename to load config from instead of passing arguments to CLI or using a Node.js script. See examples for syntax. This can also work in conjunction with `--git` or `--github` to point to remote files, and with `--key` to denote which key to select from the file., | +| `--git`\|`-g` | Git URL to load config from instead of passing arguments to CLI or using a Node.js script. See examples for syntax. | +| `--github`\|`-gh` | GitHub path to load config from instead of passing arguments to CLI or using a Node.js script. | +| `--key` \| `-k` | Key to load inside the config file. This overwrites the config key provided after the colon in `--config` (e.g. `--config scaffold.cmd.js:component`) | +| `--output` \| `-o` | Path to output to. If `--create-sub-folder` is enabled, the subfolder will be created inside this path. Default is current working directory. | +| `--templates` \| `-t` | Template files to use as input. You may provide multiple files, each of which can be a relative or absolute path, or a glob pattern for multiple file matching easily. | +| `--overwrite` \| `-w` | Enable to override output files, even if they already exist. | +| `--data` \| `-d` | Add custom data to the templates. By default, only your app name is included. | +| `--append-data` \| `-D` | Append additional custom data to the templates, which will overwrite `--data`, using an alternate syntax, which is easier to use with CLI: `-D key1=string -D key2:=raw` | +| `--create-sub-folder` \| `-s` | Create subfolder with the input name | +| `--sub-folder-name-helper` \| `-sh` | Default helper to apply to subfolder name when using `--create-sub-folder true`. | +| `--quiet` \| `-q` | Suppress output logs (Same as `--log-level none`) | +| `--log-level` \| `-l` | Determine amount of logs to display. The values are: `none \| debug \| info \| warn \| error`. The provided level will display messages of the same level or higher. | +| `--dry-run` \| `-dr` | 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. | +| `--help` \| `-h` | Show this help message | ## Examples: diff --git a/package.json b/package.json index e3fd465..0e18a61 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "date-fns": "^3.3.1", "glob": "^10.3.10", "handlebars": "^4.7.8", - "massarg": "2.0.0-pre.10" + "massarg": "2.0.0-pre.11" }, "devDependencies": { "@knodes/typedoc-plugin-pages": "^0.23.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 109a974..61f1aba 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.10 - version: 2.0.0-pre.10 + specifier: 2.0.0-pre.11 + version: 2.0.0-pre.11 devDependencies: '@knodes/typedoc-plugin-pages': @@ -3231,8 +3231,8 @@ packages: hasBin: true dev: true - /massarg@2.0.0-pre.10: - resolution: {integrity: sha512-0ngO00xzP9qxB4K0SsGoUzsc0X+/XTN+ctwflK+JNM1zuAPwq8Znucl5PfmTSFpXw764IrhNH5A1Xv+DINAweQ==} + /massarg@2.0.0-pre.11: + resolution: {integrity: sha512-MrN5ZllZyGI8DPSA8o164WgeEhfpDYnFvtA0xRoWrGJ2eCDUeyyCItUk3EB55tV3kwysqi6AAOei49xUQ5BG4w==} dependencies: zod: 3.22.4 dev: false diff --git a/src/cmd.ts b/src/cmd.ts index cc356b4..6df2ae9 100644 --- a/src/cmd.ts +++ b/src/cmd.ts @@ -35,8 +35,16 @@ export async function parseCliArgs(args = process.argv.slice(2)) { name: "config", aliases: ["c"], description: - "Filename or https git URL to load config from instead of passing arguments to CLI or using a Node.js " + - "script. See examples for syntax.", + "Filename to load config from instead of passing arguments to CLI or using a Node.js " + + "script. See examples for syntax. This can also work in conjunction with `--git` or `--github` to point " + + "to remote files, and with `--key` to denote which key to select from the file.", + }) + .option({ + name: "git", + aliases: ["g"], + description: + "Git URL to load config from instead of passing arguments to CLI or using a Node.js script. See " + + "examples for syntax.", }) .option({ name: "github", diff --git a/src/config.ts b/src/config.ts index d8c6cab..4009034 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,6 +5,7 @@ import { FileResponseHandler, LogConfig, LogLevel, + RemoteConfigLoadConfig, ScaffoldCmdConfig, ScaffoldConfig, ScaffoldConfigFile, @@ -14,6 +15,7 @@ import { log } from "./logger" import { resolve, wrapNoopResolver } from "./utils" import { getGitConfig } from "./git" +/** @internal */ export function getOptionValueForFile( config: ScaffoldConfig, filePath: string, @@ -30,6 +32,7 @@ export function getOptionValueForFile( ) } +/** @internal */ export function parseAppendData(value: string, options: ScaffoldCmdConfig): unknown { const data = options.data ?? {} const [key, val] = value.split(/\:?=/) @@ -46,7 +49,7 @@ function isWrappedWithQuotes(string: string): boolean { /** @internal */ export async function parseConfigFile(config: ScaffoldCmdConfig): Promise { - let c: ScaffoldConfig = config + let output: ScaffoldConfig = config if (config.quiet) { config.logLevel = LogLevel.none @@ -54,26 +57,34 @@ export async function parseConfigFile(config: ScaffoldCmdConfig): Promise): Promise { - const { config: configFile, isRemote, ...logConfig } = config as Required +export async function getLocalConfig(config: ConfigLoadConfig & Partial): Promise { + const { config: configFile, ...logConfig } = config as Required - if (!isRemote) { - log(logConfig, LogLevel.info, `Loading config from file ${configFile}`) - const absolutePath = path.resolve(process.cwd(), configFile) - return wrapNoopResolver(import(absolutePath)) - } + log(logConfig, LogLevel.info, `Loading config from file ${configFile}`) + const absolutePath = path.resolve(process.cwd(), configFile) + return wrapNoopResolver(import(absolutePath)) +} - const url = new URL(configFile) +/** @internal */ +export async function getRemoteConfig( + config: RemoteConfigLoadConfig & Partial, +): Promise { + const { config: configFile, git, ...logConfig } = config as Required + + log(logConfig, LogLevel.info, `Loading config from remote ${git}, file ${configFile}`) + + const url = new URL(git!) const isHttp = url.protocol === "http:" || url.protocol === "https:" const isGit = url.protocol === "git:" || (isHttp && url.pathname.endsWith(".git")) - if (isGit) { - return getGitConfig(url, logConfig) - } - - if (!isHttp) { + if (!isGit) { throw new Error(`Unsupported protocol ${url.protocol}`) } - return wrapNoopResolver(import(path.resolve(process.cwd(), configFile))) + return getGitConfig(url, configFile, logConfig) } diff --git a/src/git.ts b/src/git.ts index ffa2b4c..3217484 100644 --- a/src/git.ts +++ b/src/git.ts @@ -7,6 +7,7 @@ import { resolve, wrapNoopResolver } from "./utils" export async function getGitConfig( url: URL, + file: string, logConfig: LogConfig, ): Promise> { const repoUrl = `${url.protocol}//${url.host}${url.pathname}` @@ -22,8 +23,9 @@ export async function getGitConfig( clone.on("close", async (code) => { if (code === 0) { log(logConfig, LogLevel.info, `Loading config from git repo: ${repoUrl}`) - const hashPath = url.hash?.replace("#", "") || "scaffold.config.js" - const absolutePath = path.resolve(tmpPath, hashPath) + // TODO search for dynamic config file in repo if not provided + const filename = file || "scaffold.config.js" + const absolutePath = path.resolve(tmpPath, filename) const loadedConfig = await resolve( async () => (await import(absolutePath)).default as ScaffoldConfigMap, logConfig, diff --git a/src/types.ts b/src/types.ts index 99d9cc8..d707f7d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -322,10 +322,6 @@ export type FileResponse = T | FileResponseHandler /** @internal */ export interface ScaffoldCmdConfig { - /** - * Name to be passed to the generated files. `{{name}}` and `{{Name}}` inside contents and file names will be replaced - * accordingly. - */ name: string templates: string[] output: string @@ -337,8 +333,8 @@ export interface ScaffoldCmdConfig { logLevel: LogLevel dryRun: boolean config?: string - /** The key to use for the file which contains the template configurations. */ key?: string + git?: string github?: string } @@ -372,9 +368,11 @@ export type AsyncResolver = Resolver | R> /** @internal */ export type LogConfig = Pick -// TODO deprecat isRemote /** @internal */ -export type ConfigLoadConfig = LogConfig & Pick & { isRemote: boolean } +export type ConfigLoadConfig = LogConfig & Pick + +/** @internal */ +export type RemoteConfigLoadConfig = LogConfig & Pick /** @internal */ export type MinimalConfig = Pick diff --git a/tests/config.test.ts b/tests/config.test.ts index 2149735..f54f033 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -14,7 +14,7 @@ jest.mock("../src/git", () => { } }) -const { githubPartToUrl, parseAppendData, parseConfigFile, parseConfigSelection } = config +const { githubPartToUrl, parseAppendData, parseConfigFile } = config const blankCliConf: ScaffoldCmdConfig = { logLevel: LogLevel.none, @@ -56,46 +56,6 @@ describe("config", () => { }) }) - describe("parseConfigSelection", () => { - test("no key", () => { - expect(parseConfigSelection("scaffold.config.js")).toEqual({ - configFile: "scaffold.config.js", - key: "default", - isRemote: false, - }) - }) - test("separate key", () => { - expect(parseConfigSelection("scaffold.config.js", "component")).toEqual({ - configFile: "scaffold.config.js", - key: "component", - isRemote: false, - }) - }) - test("key override", () => { - expect(parseConfigSelection("scaffold.config.js", "main")).toEqual({ - configFile: "scaffold.config.js", - key: "main", - isRemote: false, - }) - }) - test("isRemote: false", () => { - expect(parseConfigSelection("scaffold.config.js", "main")).toEqual({ - configFile: "scaffold.config.js", - key: "main", - isRemote: false, - }) - }) - test("isRemote: true", () => { - expect( - parseConfigSelection("https://github.com/chenasraf/simple-scaffold.git#scaffold.config.js", "main"), - ).toEqual({ - configFile: "https://github.com/chenasraf/simple-scaffold.git#scaffold.config.js", - key: "main", - isRemote: true, - }) - }) - }) - describe("parseConfigFile", () => { test("normal config does not change", async () => { expect( @@ -125,9 +85,8 @@ describe("config", () => { describe("getConfig", () => { test("gets git config", async () => { - const resultFn = await config.getConfig({ - config: "https://github.com/chenasraf/simple-scaffold.git", - isRemote: true, + const resultFn = await config.getRemoteConfig({ + git: "https://github.com/chenasraf/simple-scaffold.git", logLevel: LogLevel.none, }) const result = await resolve(resultFn, blankCliConf) @@ -135,9 +94,8 @@ describe("config", () => { }) test("gets local file config", async () => { - const resultFn = await config.getConfig({ + const resultFn = await config.getLocalConfig({ config: "scaffold.config.js", - isRemote: false, logLevel: LogLevel.none, }) const result = await resolve(resultFn, {} as any)