mirror of
https://github.com/chenasraf/simple-scaffold.git
synced 2026-05-18 01:29:09 +00:00
Compare commits
17 Commits
v1.3.2
...
v1.5.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48d5af0fb6 | ||
|
|
408a940853 | ||
|
|
0256e9282b | ||
|
|
e0dc643d4e | ||
|
|
23fcaefdd9 | ||
|
|
9ea414fe1a | ||
|
|
1a3fd3d610 | ||
|
|
7f10db0d6e | ||
|
|
93f5b4a004 | ||
|
|
b74411ddb8 | ||
|
|
f961c13da1 | ||
|
|
05487f4d1e | ||
|
|
c50518a19c | ||
|
|
10ea6b4132 | ||
|
|
ce399181ab | ||
|
|
83d38073f3 | ||
|
|
6c5ba0bc91 |
5
.github/workflows/docs.yml
vendored
5
.github/workflows/docs.yml
vendored
@@ -2,11 +2,12 @@ name: Build Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, develop]
|
||||
branches: [ master, develop ]
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[skip docs]')"
|
||||
# if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[skip docs]')"
|
||||
if: "!contains(github.event.head_commit.message, '[skip docs]')"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
|
||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -1,5 +1,27 @@
|
||||
# Change Log
|
||||
|
||||
## [1.5.0-develop.2](https://github.com/chenasraf/simple-scaffold/compare/v1.5.0-develop.1...v1.5.0-develop.2) (2023-05-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* move dependency to dev dependency ([408a940](https://github.com/chenasraf/simple-scaffold/commit/408a94085366bb4e39391fcfcfa7df78b06a480f))
|
||||
|
||||
## [1.5.0-develop.1](https://github.com/chenasraf/simple-scaffold/compare/v1.4.0...v1.5.0-develop.1) (2023-05-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add github remote templates ([f961c13](https://github.com/chenasraf/simple-scaffold/commit/f961c13da15320b42540773ed958cdc3f97e4502))
|
||||
* support for remote template configs ([05487f4](https://github.com/chenasraf/simple-scaffold/commit/05487f4d1e3b05f1d695242bb54427ee2fbdf247))
|
||||
|
||||
## [1.4.0](https://github.com/chenasraf/simple-scaffold/compare/v1.3.2...v1.4.0) (2023-04-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add `--key` | `-k` to config loader ([6c5ba0b](https://github.com/chenasraf/simple-scaffold/commit/6c5ba0bc916fb1d59240d2eaa1abedc74527a974))
|
||||
|
||||
## [1.3.2](https://github.com/chenasraf/simple-scaffold/compare/v1.3.1...v1.3.2) (2023-04-28)
|
||||
|
||||
|
||||
|
||||
26
README.md
26
README.md
@@ -3,7 +3,7 @@
|
||||
<h2 align="center">
|
||||
|
||||
[GitHub](https://github.com/chenasraf/simple-scaffold) |
|
||||
[Documentation](https://casraf.dev/simple-scaffold) |
|
||||
[Documentation](https://chenasraf.github.io/simple-scaffold) |
|
||||
[NPM](https://npmjs.com/package/simple-scaffold) | [casraf.dev](https://casraf.dev)
|
||||
|
||||

|
||||
@@ -33,6 +33,8 @@ lifting for you and start building your projects faster and more efficiently tod
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Local Templates
|
||||
|
||||
The fastest way to get started is to use `npx` to immediately start a scaffold process.
|
||||
|
||||
Prepare any templates you want to use - for example, in the directory `templates/component`; and use
|
||||
@@ -72,6 +74,28 @@ export default PageWrapper: React.FC = (props) => {
|
||||
}
|
||||
```
|
||||
|
||||
### Remote Templates
|
||||
|
||||
Another quick way to start is to re-use someone else's (or your own) work using a template
|
||||
repository.
|
||||
|
||||
Here is an example for loading the example component templates in this very repository:
|
||||
|
||||
```shell
|
||||
npx simple-scaffold@latest \
|
||||
-gh chenasraf/simple-scaffold#examples/test-input/scaffold.config.js:component \
|
||||
PageWrapper
|
||||
|
||||
# equivalent to:
|
||||
npx simple-scaffold@latest \
|
||||
-c https://github.com/chenasraf/simple-scaffold.git#examples/test-input/scaffold.config.js:component \
|
||||
PageWrapper
|
||||
```
|
||||
|
||||
When template name (`:component`) is omitted, `default` is used.
|
||||
|
||||
See more at the [CLI documentation](https://chenasraf.github.io/simple-scaffold/pages/cli.html)
|
||||
|
||||
## Documentation
|
||||
|
||||
See full documentation [here](https://chenasraf.github.io/simple-scaffold).
|
||||
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
},
|
||||
component: {
|
||||
templates: ["examples/test-input/Component"],
|
||||
output: "examples/test-output",
|
||||
output: "examples/test-output/component",
|
||||
data: { property: "myProp", value: "10" },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "simple-scaffold",
|
||||
"version": "1.3.1",
|
||||
"version": "1.5.0",
|
||||
"description": "A simple command to generate any file structure, from single components to entire app boilerplates.",
|
||||
"homepage": "https://casraf.dev/simple-scaffold",
|
||||
"homepage": "https://chenasraf.github.io/simple-scaffold",
|
||||
"repository": "https://github.com/chenasraf/simple-scaffold.git",
|
||||
"author": "Chen Asraf <inbox@casraf.com>",
|
||||
"license": "MIT",
|
||||
@@ -40,7 +40,6 @@
|
||||
"handlebars": "^4.7.7",
|
||||
"lodash": "^4.17.21",
|
||||
"massarg": "^1.0.7-pre.1",
|
||||
"semantic-release-conventional-commits": "^3.0.0",
|
||||
"util.promisify": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -59,6 +58,7 @@
|
||||
"mock-fs": "^5.2.0",
|
||||
"rimraf": "^5.0.0",
|
||||
"semantic-release": "^21.0.1",
|
||||
"semantic-release-conventional-commits": "^3.0.0",
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typedoc": "^0.24.6",
|
||||
|
||||
117
pages/cli.md
117
pages/cli.md
@@ -1,68 +1,73 @@
|
||||
## Available flags
|
||||
|
||||
The following is the help text from the `simple-scaffold` binary. To see this and more information
|
||||
anytime, add the `-h` or `--help` flag to your call, e.g. `npx simple-scaffold@latest -h`.
|
||||
|
||||
```text
|
||||
Usage: simple-scaffold [options]
|
||||
|
||||
Create structured files based on templates.
|
||||
|
||||
Options:
|
||||
|
||||
--help|-h Display help information
|
||||
|
||||
--name|-n Name to be passed to the generated files. {{name}} and
|
||||
{{Name}} inside contents and file names will be replaced
|
||||
accordingly.
|
||||
|
||||
--config|-c Filename to load config from instead of passing
|
||||
arguments to CLI or using a Node.js script. You may pass a
|
||||
JSON or JS file, with a relative or absolute path.
|
||||
|
||||
--output|-o Path to output to. If --create-sub-folder is enabled,
|
||||
the subfolder will be created inside this path.
|
||||
(default: current dir)
|
||||
|
||||
--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. (default: false)
|
||||
|
||||
--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
|
||||
(default: false)
|
||||
|
||||
--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 --verbose 0)
|
||||
(default: false)
|
||||
|
||||
--verbose|-v Determine amount of logs to display. The values are:
|
||||
0 (none) | 1 (debug) | 2 (info) | 3 (warn) | 4
|
||||
(error). The provided level will display messages of
|
||||
the same level or higher. (default:
|
||||
2)
|
||||
|
||||
--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.
|
||||
(default: false)
|
||||
```
|
||||
|
||||
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 | |
|
||||
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `--help`\|`-h` | Display help information |
|
||||
| `--name`\|`-n` | Name to be passed to the generated files. {{name}} and {{Name}} inside contents and file names will be replaced accordingly. |
|
||||
| `--config`\|`-c` | Filename or HTTPS git URL to load config from instead of passing arguments to CLI or using a Node.js script. |
|
||||
| `--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: current dir) |
|
||||
| `--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. (default: false) |
|
||||
| `--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 (default: false) |
|
||||
| `--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 --verbose 0) (default: false) |
|
||||
| `--verbose`\|`-v` | Determine amount of logs to display. The values are: 0 (none) \| 1 (debug) \| 2 (info) \| 3 (warn) \| 4 (error). The provided level will display messages of the same level or higher. (default: 2) |
|
||||
| `--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. (default: false) |
|
||||
|
||||
## Examples:
|
||||
|
||||
> See
|
||||
> [Configuration Files](https://chenasraf.githun.io/simple-scaffold/pages/docs/configuration_files.md)
|
||||
> [Configuration Files](https://chenasraf.github.io/simple-scaffold/pages/docs/configuration_files.md)
|
||||
> for organizing multiple scaffold types into easy-to-maintain files
|
||||
|
||||
Usage with config file
|
||||
|
||||
```shell
|
||||
$ simple-scaffold -c scaffold.cmd.js --key component
|
||||
```
|
||||
|
||||
Usage with GitHub config file
|
||||
|
||||
```shell
|
||||
$ simple-scaffold -gh chenasraf/simple-scaffold --key component
|
||||
```
|
||||
|
||||
Usage with https git URL (for non-GitHub)
|
||||
|
||||
```shell
|
||||
$ simple-scaffold -c \
|
||||
https://example.com/user/template.git#scaffold.cmd.js --key component
|
||||
```
|
||||
|
||||
Full syntax with config path and template key (applicable to all above methods)
|
||||
|
||||
```shell
|
||||
$ simple-scaffold -c scaffold.cmd.js:component MyComponent
|
||||
```
|
||||
|
||||
Excluded template key, assumes 'default' key
|
||||
|
||||
```shell
|
||||
$ simple-scaffold -c scaffold.cmd.js MyComponent
|
||||
```
|
||||
|
||||
Shortest syntax for GitHub, assumes file 'scaffold.cmd.js' and template key 'default'
|
||||
|
||||
```shell
|
||||
$ simple-scaffold -gh chenasraf/simple-scaffold MyComponent
|
||||
```
|
||||
|
||||
You can also add this as a script in your `package.json`:
|
||||
|
||||
```json
|
||||
|
||||
@@ -60,7 +60,13 @@ module.exports = {
|
||||
## Using a config file
|
||||
|
||||
Once your config is created, you can use it by providing the file name to the `--config` (or `-c`
|
||||
for brevity), followed by a colon, then your scaffold config name. For example:
|
||||
for brevity), optionally followed by a colon, then your scaffold config name.
|
||||
|
||||
```shell
|
||||
simple-scaffold -c <file>[:<template_key>]
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```shell
|
||||
simple-scaffold -c scaffold.json:component MyComponentName
|
||||
@@ -84,3 +90,45 @@ And then:
|
||||
# will use 'default' template
|
||||
simple-scaffold -c scaffold.json MyComponentName
|
||||
```
|
||||
|
||||
## Remote Templates
|
||||
|
||||
You can load template groups remotely, similar to how you would pass a config normally.
|
||||
|
||||
The main difference is the templates will be hosted on a remote location such as a git server, and
|
||||
not locally in your project. This can be done to easily share & reuse templates.
|
||||
|
||||
When passing a git URL to `--config`, you will clone that repo and use the files there as template.
|
||||
|
||||
The syntax is as follows:
|
||||
|
||||
```shell
|
||||
simple-scaffold -c <git_url>[#<git_file>][:<template_key>]
|
||||
```
|
||||
|
||||
For example, to use this repository's example as base:
|
||||
|
||||
```shell
|
||||
simple-scaffold -c https://github.com/chenasraf/simple-scaffold.git#examples/test-input/scaffold.config.js:component
|
||||
```
|
||||
|
||||
When the filename is omitted, `/scaffold.config.js` will be used as default.
|
||||
|
||||
When the template_key is ommitted, `default` will be used as default.
|
||||
|
||||
### GitHub Templates
|
||||
|
||||
As a shorter alternative to the above example, you can use `--github` or `-gh` to reference a GitHub
|
||||
URL without specifying the whole path.
|
||||
|
||||
The syntax is as follows:
|
||||
|
||||
```shell
|
||||
simple-scaffold -gh <username>/<project_name>[#<git_file>][:<template_key>]
|
||||
```
|
||||
|
||||
This example is equivalent to the above, just shorter to write:
|
||||
|
||||
```shell
|
||||
simple-scaffold -c chenasraf/simple-scaffold#examples/test-input/scaffold.config.js:component
|
||||
```
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
const releaseRules = [
|
||||
{ type: "feat", section: "Features", release: "minor" },
|
||||
{ type: "docs", section: "Misc", release: "patch" },
|
||||
{ type: "docs", section: "Build", release: false },
|
||||
{ type: "fix", section: "Bug Fixes", release: "patch" },
|
||||
{ type: "refactor", section: "Misc", release: "patch" },
|
||||
{ type: "docs", section: "Build", release: "patch" },
|
||||
{ type: "perf", section: "Misc", release: "patch" },
|
||||
{ type: "build", section: "Build", release: "patch" },
|
||||
{ type: "chore", section: "Misc", release: "patch" },
|
||||
{ type: "test", section: "Misc", release: "patch" },
|
||||
{ type: "test", section: "Tests", release: "patch" },
|
||||
]
|
||||
|
||||
/** @type {import('semantic-release').Options} */
|
||||
@@ -17,6 +16,7 @@ module.exports = {
|
||||
"master",
|
||||
"next",
|
||||
"next-major",
|
||||
{ name: "develop", prerelease: true },
|
||||
{ name: "beta", prerelease: true },
|
||||
{ name: "alpha", prerelease: true },
|
||||
],
|
||||
@@ -60,17 +60,17 @@ module.exports = {
|
||||
pkgRoot: "dist",
|
||||
},
|
||||
],
|
||||
[
|
||||
"@semantic-release/github",
|
||||
{
|
||||
assets: ["package.tgz"],
|
||||
},
|
||||
],
|
||||
[
|
||||
"@semantic-release/git",
|
||||
{
|
||||
assets: ["CHANGELOG.md", "package.json"],
|
||||
},
|
||||
],
|
||||
[
|
||||
"@semantic-release/github",
|
||||
{
|
||||
assets: ["package.tgz"],
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
|
||||
47
src/cmd.ts
47
src/cmd.ts
@@ -9,12 +9,12 @@ import { parseAppendData, parseConfig } from "./utils"
|
||||
|
||||
export async function parseCliArgs(args = process.argv.slice(2)) {
|
||||
const pkg = JSON.parse((await fs.readFile(path.join(__dirname, "package.json"))).toString())
|
||||
const isConfig = args.includes("--config") || args.includes("-c")
|
||||
const isConfig = args.includes("--config") || args.includes("-c") || args.includes("--github") || args.includes("-gh")
|
||||
|
||||
return (
|
||||
massarg<ScaffoldCmdConfig>()
|
||||
.main((config) => {
|
||||
const _config = parseConfig(config)
|
||||
.main(async (config) => {
|
||||
const _config = await parseConfig(config)
|
||||
return Scaffold(_config)
|
||||
})
|
||||
.option({
|
||||
@@ -29,7 +29,19 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
|
||||
name: "config",
|
||||
aliases: ["c"],
|
||||
description:
|
||||
"Filename to load config from instead of passing arguments to CLI or using a Node.js script. You may pass a JSON or JS file, with a relative or absolute path.",
|
||||
"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.",
|
||||
})
|
||||
.option({
|
||||
name: "github",
|
||||
aliases: ["gh"],
|
||||
description:
|
||||
"GitHub path to load config from instead of passing arguments to CLI or using a Node.js script. See examples for syntax.",
|
||||
})
|
||||
.option({
|
||||
name: "key",
|
||||
aliases: ["k"],
|
||||
description:
|
||||
"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)",
|
||||
})
|
||||
.option({
|
||||
name: "output",
|
||||
@@ -109,16 +121,41 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
|
||||
// description: "Usage",
|
||||
// output: "",
|
||||
// })
|
||||
.example({
|
||||
description: "Usage with config file",
|
||||
input: "simple-scaffold -c scaffold.cmd.js --key component",
|
||||
})
|
||||
.example({
|
||||
description: "Usage with GitHub config file",
|
||||
input: "simple-scaffold -gh chenasraf/simple-scaffold --key component",
|
||||
})
|
||||
.example({
|
||||
description: "Usage with https git URL (for non-GitHub)",
|
||||
input: "simple-scaffold -c https://example.com/user/template.git#scaffold.cmd.js --key component",
|
||||
})
|
||||
.example({
|
||||
description: "Full syntax with config path and template key (applicable to all above methods)",
|
||||
input: "simple-scaffold -c scaffold.cmd.js:component MyComponent",
|
||||
})
|
||||
.example({
|
||||
description: "Excluded template key, assumes 'default' key",
|
||||
input: "simple-scaffold -c scaffold.cmd.js MyComponent",
|
||||
})
|
||||
.example({
|
||||
description: "Shortest syntax for GitHub, assumes file 'scaffold.cmd.js' and template key 'default'",
|
||||
input: "simple-scaffold -gh chenasraf/simple-scaffold MyComponent",
|
||||
})
|
||||
.help({
|
||||
binName: "simple-scaffold",
|
||||
useGlobalColumns: true,
|
||||
usageExample: "[options]",
|
||||
printWidth: 100,
|
||||
header: [`Create structured files based on templates.`].join("\n"),
|
||||
footer: [
|
||||
`Version: ${pkg.version}`,
|
||||
`Copyright © Chen Asraf 2017-${new Date().getFullYear()}`,
|
||||
``,
|
||||
`Documentation: ${chalk.underline`https://casraf.dev/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"),
|
||||
|
||||
22
src/types.ts
22
src/types.ts
@@ -3,7 +3,8 @@ import { HelperDelegate } from "handlebars/runtime"
|
||||
/**
|
||||
* The config object for defining a scaffolding group.
|
||||
*
|
||||
* @see https://github.com/chenasraf/simple-scaffold#readme
|
||||
* @see {@link https://chenasraf.github.io/simple-scaffold/pages/node.html | Node.js usage}
|
||||
* @see {@link https://chenasraf.github.io/simple-scaffold/pages/cli.html | CLI usage}
|
||||
* @see {@link DefaultHelpers}
|
||||
* @see {@link CaseHelpers}
|
||||
* @see {@link DateHelpers}
|
||||
@@ -131,10 +132,8 @@ export interface ScaffoldConfig {
|
||||
* @see {@link DefaultHelpers}
|
||||
* @see {@link CaseHelpers}
|
||||
* @see {@link DateHelpers}
|
||||
* @see https://casraf.dev/simple-scaffold#helpers
|
||||
* @see https://casraf.dev/simple-scaffold#built-in-helpers
|
||||
* @see https://handlebarsjs.com/guide/#custom-helpers
|
||||
*/
|
||||
* @see {@link https://chenasraf.github.io/simple-scaffold/pages/templates.html | Templates}
|
||||
* */
|
||||
helpers?: Record<string, Helper>
|
||||
|
||||
/**
|
||||
@@ -337,6 +336,19 @@ export interface ScaffoldCmdConfig {
|
||||
verbose: LogLevel
|
||||
dryRun: boolean
|
||||
config?: string
|
||||
key?: string
|
||||
github?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A mapping of scaffold template keys to their configurations.
|
||||
*
|
||||
* Each configuration is a {@link ScaffoldConfig} object.
|
||||
*
|
||||
* The key is the name of the template, and the value is the configuration for that template.
|
||||
*
|
||||
* When no template key is provided to the scaffold command, the "default" template is used.
|
||||
*
|
||||
* @see {@link ScaffoldConfig}
|
||||
*/
|
||||
export type ScaffoldConfigFile = Record<string, ScaffoldConfig>
|
||||
|
||||
107
src/utils.ts
107
src/utils.ts
@@ -23,6 +23,8 @@ import dtFormat from "date-fns/format"
|
||||
import dtParseISO from "date-fns/parseISO"
|
||||
import { glob, hasMagic } from "glob"
|
||||
import { OptionsBase } from "massarg/types"
|
||||
import { spawn } from "node:child_process"
|
||||
import os from "node:os"
|
||||
|
||||
const dateFns = {
|
||||
add: dtAdd,
|
||||
@@ -99,10 +101,14 @@ export function handleErr(err: NodeJS.ErrnoException | null): void {
|
||||
if (err) throw err
|
||||
}
|
||||
|
||||
export function log(config: ScaffoldConfig, level: LogLevel, ...obj: any[]): void {
|
||||
/** @internal */
|
||||
export type LogConfig = Pick<ScaffoldConfig, "quiet" | "verbose">
|
||||
|
||||
export function log(config: LogConfig, level: LogLevel, ...obj: any[]): void {
|
||||
if (config.quiet || config.verbose === LogLevel.None || level < (config.verbose ?? LogLevel.Info)) {
|
||||
return
|
||||
}
|
||||
|
||||
const levelColor: Record<LogLevel, keyof typeof chalk> = {
|
||||
[LogLevel.None]: "reset",
|
||||
[LogLevel.Debug]: "blue",
|
||||
@@ -110,6 +116,7 @@ export function log(config: ScaffoldConfig, level: LogLevel, ...obj: any[]): voi
|
||||
[LogLevel.Warning]: "yellow",
|
||||
[LogLevel.Error]: "red",
|
||||
}
|
||||
|
||||
const chalkFn: any = chalk[levelColor[level]]
|
||||
const key: "log" | "warn" | "error" = level === LogLevel.Error ? "error" : level === LogLevel.Warning ? "warn" : "log"
|
||||
const logFn: any = console[key]
|
||||
@@ -370,16 +377,18 @@ export function logInitStep(config: ScaffoldConfig): void {
|
||||
name: config.name,
|
||||
templates: config.templates,
|
||||
output: config.output,
|
||||
createSubfolder: config.createSubFolder,
|
||||
createSubFolder: config.createSubFolder,
|
||||
data: config.data,
|
||||
overwrite: config.overwrite,
|
||||
quiet: config.quiet,
|
||||
subFolderTransformHelper: config.subFolderNameHelper,
|
||||
subFolderNameHelper: config.subFolderNameHelper,
|
||||
helpers: Object.keys(config.helpers ?? {}),
|
||||
verbose: `${config.verbose} (${Object.keys(LogLevel).find(
|
||||
(k) => (LogLevel[k as any] as unknown as number) === config.verbose!,
|
||||
)})`,
|
||||
})
|
||||
dryRun: config.dryRun,
|
||||
beforeWrite: config.beforeWrite,
|
||||
} as Record<keyof ScaffoldConfig, unknown>)
|
||||
log(config, LogLevel.Info, "Data:", config.data)
|
||||
}
|
||||
|
||||
@@ -398,20 +407,36 @@ function isWrappedWithQuotes(string: string): boolean {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function parseConfig(config: ScaffoldCmdConfig & OptionsBase): ScaffoldConfig {
|
||||
export async function parseConfig(config: ScaffoldCmdConfig & OptionsBase): Promise<ScaffoldConfig> {
|
||||
let c: ScaffoldConfig = config
|
||||
if (config.github) {
|
||||
log(config, LogLevel.Info, `Loading config from github ${config.github}`)
|
||||
const gitUrl = new URL(`https://github.com/${config.github}`)
|
||||
if (!gitUrl.pathname.endsWith(".git")) {
|
||||
gitUrl.pathname += ".git"
|
||||
}
|
||||
config.config = gitUrl.toString()
|
||||
}
|
||||
|
||||
if (config.config) {
|
||||
const [configFile, template = "default"] = config.config.split(":")
|
||||
const configImport: ScaffoldConfigFile = require(path.resolve(process.cwd(), configFile))
|
||||
if (!configImport[template]) {
|
||||
throw new Error(`Template "${template}" not found in ${configFile}`)
|
||||
const isUrl = config.config.includes("://")
|
||||
|
||||
const hasColonToken = (!isUrl && config.config.includes(":")) || (isUrl && count(config.config, ":") > 1)
|
||||
const colonIndex = config.config.lastIndexOf(":")
|
||||
const [configFile, templateKey = "default"] = hasColonToken
|
||||
? [config.config.substring(0, colonIndex), config.config.substring(colonIndex + 1)]
|
||||
: [config.config, undefined]
|
||||
const key = (config.key ?? templateKey) || "default"
|
||||
log(config, LogLevel.Info, `Loading config from ${configFile} with key ${key}`)
|
||||
const configImport = await getConfig({ config: configFile, quiet: config.quiet, verbose: config.verbose })
|
||||
if (!configImport[key]) {
|
||||
throw new Error(`Template "${key}" not found in ${configFile}`)
|
||||
}
|
||||
c = {
|
||||
...config,
|
||||
...configImport[template],
|
||||
...configImport[key],
|
||||
data: {
|
||||
...configImport[template].data,
|
||||
...configImport[key].data,
|
||||
...config.data,
|
||||
},
|
||||
}
|
||||
@@ -421,3 +446,63 @@ export function parseConfig(config: ScaffoldCmdConfig & OptionsBase): ScaffoldCo
|
||||
delete config.appendData
|
||||
return c
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function getConfig(
|
||||
config: Pick<ScaffoldCmdConfig, "quiet" | "verbose" | "config">,
|
||||
): Promise<ScaffoldConfigFile> {
|
||||
const { config: configFile, ...logConfig } = config as Required<typeof config>
|
||||
const url = new URL(configFile)
|
||||
|
||||
if (url.protocol === "file:") {
|
||||
log(logConfig, LogLevel.Info, `Loading config from file ${configFile}`)
|
||||
const absolutePath = path.resolve(process.cwd(), configFile)
|
||||
return import(absolutePath)
|
||||
}
|
||||
|
||||
const isHttp = url.protocol === "http:" || url.protocol === "https:"
|
||||
const isGit = url.protocol === "git:" || (isHttp && url.pathname.endsWith(".git"))
|
||||
|
||||
if (isHttp || isGit) {
|
||||
if (isGit) {
|
||||
const repoUrl = `${url.protocol}//${url.host}${url.pathname}`
|
||||
log(logConfig, LogLevel.Info, `Cloning git repo ${repoUrl}`)
|
||||
const tmpPath = path.resolve(os.tmpdir(), `scaffold-config-${Date.now()}`)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const clone = spawn("git", ["clone", "--depth", "1", repoUrl, tmpPath])
|
||||
|
||||
clone.on("error", reject)
|
||||
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)
|
||||
const loadedConfig = (await import(absolutePath)).default as ScaffoldConfigFile
|
||||
log(logConfig, LogLevel.Info, `Loaded config from git`)
|
||||
log(logConfig, LogLevel.Debug, `Raw config:`, loadedConfig)
|
||||
const fixedConfig: ScaffoldConfigFile = Object.fromEntries(
|
||||
Object.entries(loadedConfig).map(([k, v]) => [
|
||||
k,
|
||||
// use absolute paths for template as config is necessarily in another directory
|
||||
{ ...v, templates: v.templates.map((t) => path.resolve(tmpPath, t)) },
|
||||
]),
|
||||
)
|
||||
|
||||
resolve(fixedConfig)
|
||||
} else {
|
||||
reject(new Error(`Git clone failed with code ${code}`))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported protocol ${url.protocol}`)
|
||||
}
|
||||
|
||||
return import(path.resolve(process.cwd(), configFile))
|
||||
}
|
||||
|
||||
function count(string: string, substring: string): number {
|
||||
return string.split(substring).length - 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user