mirror of
https://github.com/chenasraf/simple-scaffold.git
synced 2026-05-18 01:29:09 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7be79fdcaf | ||
|
|
6afbcdc258 | ||
|
|
1a3fd3d610 | ||
|
|
7f10db0d6e | ||
|
|
93f5b4a004 | ||
|
|
b74411ddb8 | ||
|
|
f961c13da1 | ||
|
|
05487f4d1e | ||
|
|
c50518a19c | ||
|
|
10ea6b4132 | ||
|
|
ce399181ab | ||
|
|
83d38073f3 | ||
|
|
6c5ba0bc91 | ||
|
|
2c4eccd800 | ||
|
|
2c23fa9dbb | ||
|
|
0bef2df5f3 | ||
|
|
398a5d723b | ||
|
|
c7e2ef862c | ||
|
|
36dd27e480 | ||
|
|
6e19a86190 | ||
|
|
eba7897f84 |
5
.github/workflows/docs.yml
vendored
5
.github/workflows/docs.yml
vendored
@@ -2,11 +2,12 @@ name: Build Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
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
|
||||
|
||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -2,7 +2,7 @@ name: Semantic Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, develop, feat/*, fix/*]
|
||||
branches: [ master, develop, feat/*, fix/* ]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -15,6 +15,7 @@ jobs:
|
||||
- run: yarn install --frozen-lockfile
|
||||
- run: yarn test
|
||||
- run: yarn build
|
||||
- run: cd ./dist && yarn pack --filename=../package.tgz
|
||||
- run: yarn semantic-release
|
||||
if: "!contains(github.event.head_commit.message, '[skip publish]')"
|
||||
env:
|
||||
|
||||
48
CHANGELOG.md
48
CHANGELOG.md
@@ -1,17 +1,57 @@
|
||||
# Change Log
|
||||
|
||||
## [1.3.0](https://github.com/chenasraf/simple-scaffold/compare/v1.2.0...v1.3.0) (2023-04-25)
|
||||
## [1.5.0](https://github.com/chenasraf/simple-scaffold/compare/v1.4.0...v1.5.0) (2023-05-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* load scaffold config from files ([c398976](https://github.com/chenasraf/simple-scaffold/commit/c3989769fee445c9183ff5e5b3892c4e9fb66a9e))
|
||||
* 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.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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* config option should not be mandatory ([3db6a91](https://github.com/chenasraf/simple-scaffold/commit/3db6a918f13d9300efa2fcb4a356d004475ab91c))
|
||||
* export config file type ([4302eb5](https://github.com/chenasraf/simple-scaffold/commit/4302eb5ce35ed6cf1dc80dfb92790c3fdd96f963))
|
||||
* release build ([2c23fa9](https://github.com/chenasraf/simple-scaffold/commit/2c23fa9dbb310cd0a31f09606798f96b95d66779))
|
||||
* release build asset ([0bef2df](https://github.com/chenasraf/simple-scaffold/commit/0bef2df5f3aa800ad5f1094c0996108db9acce51))
|
||||
|
||||
## [1.3.1](https://github.com/chenasraf/simple-scaffold/compare/v1.3.0...v1.3.1) (2023-04-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* docs ([6e19a86](https://github.com/chenasraf/simple-scaffold/commit/6e19a86190dd924058a48448aa6463569ef1125f))
|
||||
* remove old peer-dep ([c7e2ef8](https://github.com/chenasraf/simple-scaffold/commit/c7e2ef862cb658feb1071ac120b185d8b34d6dd3))
|
||||
|
||||
## [1.3.0](https://github.com/chenasraf/simple-scaffold/compare/v1.2.0...v1.3.0) (2023-04-25)
|
||||
|
||||
### Features
|
||||
|
||||
- load scaffold config from files
|
||||
([c398976](https://github.com/chenasraf/simple-scaffold/commit/c3989769fee445c9183ff5e5b3892c4e9fb66a9e))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- config option should not be mandatory
|
||||
([3db6a91](https://github.com/chenasraf/simple-scaffold/commit/3db6a918f13d9300efa2fcb4a356d004475ab91c))
|
||||
- export config file type
|
||||
([4302eb5](https://github.com/chenasraf/simple-scaffold/commit/4302eb5ce35ed6cf1dc80dfb92790c3fdd96f963))
|
||||
|
||||
## [1.2.0](https://github.com/chenasraf/simple-scaffold/compare/v1.1.4...v1.2.0) (2023-04-24)
|
||||
|
||||
|
||||
24
README.md
24
README.md
@@ -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,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-scaffold",
|
||||
"version": "1.3.0",
|
||||
"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",
|
||||
"repository": "https://github.com/chenasraf/simple-scaffold.git",
|
||||
@@ -63,8 +63,5 @@
|
||||
"ts-node": "^10.9.1",
|
||||
"typedoc": "^0.24.6",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"doc-theme": "file:./doc-theme"
|
||||
}
|
||||
}
|
||||
|
||||
96
pages/cli.md
96
pages/cli.md
@@ -16,9 +16,99 @@ Options:
|
||||
{{Name}} inside contents and file names will be replaced
|
||||
accordingly.
|
||||
|
||||
--config|-c Filename to load config from instead of passing
|
||||
--config|-c Filename or https git URL 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
|
||||
|
||||
--github|-gh GitHub path 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.
|
||||
GitHub path (e.g. username/package#scaffold.config.js). You
|
||||
may also optionally add a key (same as passing --key) to load
|
||||
from inside the config.
|
||||
|
||||
--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)
|
||||
|
||||
Version: 1.4.0
|
||||
Copyright © Chen Asraf 2017-2023
|
||||
|
||||
Documentation: https://casraf.dev/simple-scaffold
|
||||
NPM: https://npmjs.com/package/simple-scaffold
|
||||
GitHub: https://github.com/chenasraf/simple-scaffold
|
||||
|
||||
✨ Done in 11.00s.
|
||||
❯ yarn build-cmd -h
|
||||
yarn run v1.22.19
|
||||
$ yarn build && yarn cmd -h
|
||||
$ yarn clean && tsc && chmod -R +x ./dist && cp ./package.json ./README.md ./dist/
|
||||
$ rimraf dist/
|
||||
$ node --trace-warnings dist/cmd.js -h
|
||||
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 or https git URL 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, or a
|
||||
fully qualified git repository URL.
|
||||
|
||||
--github|-gh GitHub path to load config from instead of passing
|
||||
arguments to CLI or using a Node.js script. You may pass a
|
||||
GitHub path (e.g. username/package#scaffold.config.js). You
|
||||
may also optionally add a key (same as passing --key) to load
|
||||
from inside the config.
|
||||
|
||||
--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.
|
||||
@@ -60,7 +150,7 @@ Options:
|
||||
```
|
||||
|
||||
> See
|
||||
> [Configuration Files](https://chenasraf.githun.io/simple-scaffold/pages/docs/docs/configuration_files.md)
|
||||
> [Configuration Files](https://chenasraf.githun.io/simple-scaffold/pages/docs/configuration_files.md)
|
||||
> for organizing multiple scaffold types into easy-to-maintain files
|
||||
|
||||
You can also add this as a script in your `package.json`:
|
||||
|
||||
@@ -19,7 +19,7 @@ example:
|
||||
```
|
||||
|
||||
The configuration contents are identical to the
|
||||
[Node.js configuration structure](https://chenasraf.githun.io/simple-scaffold/pages/docs/node.md):
|
||||
[Node.js configuration structure](https://chenasraf.github.io/simple-scaffold/pages/node.md):
|
||||
|
||||
```ts
|
||||
interface ScaffoldConfig {
|
||||
@@ -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 },
|
||||
],
|
||||
@@ -57,13 +57,7 @@ module.exports = {
|
||||
[
|
||||
"@semantic-release/npm",
|
||||
{
|
||||
packageRoot: "dist",
|
||||
},
|
||||
],
|
||||
[
|
||||
"@semantic-release/github",
|
||||
{
|
||||
assets: ["package.tgz"],
|
||||
pkgRoot: "dist",
|
||||
},
|
||||
],
|
||||
[
|
||||
@@ -72,5 +66,11 @@ module.exports = {
|
||||
assets: ["CHANGELOG.md", "package.json"],
|
||||
},
|
||||
],
|
||||
[
|
||||
"@semantic-release/github",
|
||||
{
|
||||
assets: ["package.tgz"],
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
|
||||
23
src/cmd.ts
23
src/cmd.ts
@@ -1,21 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
import massarg from "massarg"
|
||||
import chalk from "chalk"
|
||||
import { LogLevel, ScaffoldCmdConfig, ScaffoldConfig } from "./types"
|
||||
import { LogLevel, ScaffoldCmdConfig } from "./types"
|
||||
import { Scaffold } from "./scaffold"
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
import { parseAppendData, parseConfig } from "./utils"
|
||||
import { OptionsBase } from "massarg/types"
|
||||
|
||||
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({
|
||||
@@ -30,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. You may pass a JSON or JS file with a relative or absolute path, or a fully qualified git repository URL.",
|
||||
})
|
||||
.option({
|
||||
name: "github",
|
||||
aliases: ["gh"],
|
||||
description:
|
||||
"GitHub path to load config from instead of passing arguments to CLI or using a Node.js script. You may pass a GitHub path (e.g. username/package#scaffold.config.js). You may also optionally add a key (same as passing --key) to load from inside the config.",
|
||||
})
|
||||
.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",
|
||||
|
||||
@@ -337,6 +337,8 @@ export interface ScaffoldCmdConfig {
|
||||
verbose: LogLevel
|
||||
dryRun: boolean
|
||||
config?: string
|
||||
key?: string
|
||||
github?: string
|
||||
}
|
||||
|
||||
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