mirror of
https://github.com/chenasraf/simple-scaffold.git
synced 2026-05-18 01:29:09 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adc95809ba | ||
| 98b326c843 | |||
| ddc115a037 | |||
| 19e7b0f0c3 | |||
|
|
f883571daa | ||
|
|
be3068a533 | ||
|
|
8acc660dea | ||
|
|
df6c351cb0 | ||
|
|
5f810e2116 | ||
| d579c09c11 | |||
| 3765398ab9 |
3
.github/workflows/docs.yml
vendored
3
.github/workflows/docs.yml
vendored
@@ -1,5 +1,8 @@
|
||||
name: Documentation
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, pre, develop]
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,5 +1,21 @@
|
||||
# Change Log
|
||||
|
||||
## [2.2.1](https://github.com/chenasraf/simple-scaffold/compare/v2.2.0...v2.2.1) (2024-04-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* beforeWrite from config files ([98b326c](https://github.com/chenasraf/simple-scaffold/commit/98b326c84346162f379af46bc5aefb69df8be515))
|
||||
* use console.info for handlebars parse warning ([19e7b0f](https://github.com/chenasraf/simple-scaffold/commit/19e7b0f0c35c1b79a98781bdec9f54354123d8e0))
|
||||
|
||||
# [2.2.0](https://github.com/chenasraf/simple-scaffold/compare/v2.1.0...v2.2.0) (2024-02-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* `list` command ([d579c09](https://github.com/chenasraf/simple-scaffold/commit/d579c09c11f2149fe7bb4515297c1287fa67083e))
|
||||
* add `--before-write` cli option ([#89](https://github.com/chenasraf/simple-scaffold/issues/89)) ([5f810e2](https://github.com/chenasraf/simple-scaffold/commit/5f810e21160816bc683cc0f375de318ff874871c))
|
||||
|
||||
# [2.1.0](https://github.com/chenasraf/simple-scaffold/compare/v2.0.2...v2.1.0) (2024-02-12)
|
||||
|
||||
|
||||
|
||||
@@ -11,24 +11,57 @@ 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 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. |
|
||||
| `--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 |
|
||||
| `--version` \| `-v` | Display version. |
|
||||
| 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 directory to load config from |
|
||||
| `--git`\|`-g` | Git URL or GitHub path to load a template from. |
|
||||
| `--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. |
|
||||
| `--before-write` \| `-B` | Run a script before writing the files. This can be a command or a path to a file. A temporary file path will be passed to the given command and the command should return a string for the final output. |
|
||||
| `--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 |
|
||||
| `--version` \| `-v` | Display version. |
|
||||
|
||||
### Before Write option
|
||||
|
||||
This option allows you to preprocess a file before it is being written, such as running a formatter,
|
||||
linter or other commands.
|
||||
|
||||
To use this option, pass it the command you would like to run. The following tokens will be replaced
|
||||
in your string:
|
||||
|
||||
- `{{path}}` - the temporary file path for you to read from
|
||||
- `{{rawpath}}` - a different file path containing the raw file contents **before** they were
|
||||
handled by Handlebars.js.
|
||||
|
||||
If none of these tokens are found, the regular (non-raw) path will be appended to the end of the
|
||||
command.
|
||||
|
||||
```shell
|
||||
simple-scaffold -c . --before-write prettier
|
||||
# command: prettier /tmp/somefile
|
||||
|
||||
simple-scaffold -c . --before-write 'cat {{path}} | my-linter'
|
||||
# command: cat /tmp/somefile | my-linter
|
||||
```
|
||||
|
||||
The command should return the string to write to the file through standard output (stdout), and not
|
||||
re-write the tmp file as it is not used for writing. Returning an empty string (after trimming) will
|
||||
discard the result and write the original file contents.
|
||||
|
||||
See
|
||||
[beforeWrite](https://chenasraf.github.io/simple-scaffold/docs/api/interfaces/ScaffoldConfig#beforewrite)
|
||||
Node.js API for more details. Instead of returning `undefined` to keep the default behavior, you can
|
||||
output `''` for the same effect.
|
||||
|
||||
## Examples:
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
title: Node.js Usage
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
You can build the scaffold yourself, if you want to create more complex arguments, scaffold groups,
|
||||
etc - simply pass a config object to the Scaffold function when you are ready to start.
|
||||
|
||||
@@ -33,6 +35,19 @@ interface ScaffoldConfig {
|
||||
}
|
||||
```
|
||||
|
||||
### Before Write option
|
||||
|
||||
This option allows you to preprocess a file before it is being written, such as running a formatter,
|
||||
linter or other commands.
|
||||
|
||||
To use this option, you can run any async/blocking command, and return a string as the final output
|
||||
to be used as the file contents.
|
||||
|
||||
Returning `undefined` will keep the file contents as-is, after normal Handlebars.js procesing by
|
||||
Simple Scaffold.
|
||||
|
||||
## Example
|
||||
|
||||
This is an example of loading a complete scaffold via Node.js:
|
||||
|
||||
```typescript
|
||||
@@ -50,6 +65,8 @@ const config = {
|
||||
helpers: {
|
||||
twice: (text) => [text, text].join(" ")
|
||||
},
|
||||
// return a string to replace the final file contents after pre-processing, or `undefined`
|
||||
// to keep it as-is
|
||||
beforeWrite: (content, rawContent, outputPath) => content.toString().toUpperCase()
|
||||
}
|
||||
|
||||
|
||||
74
docs/pnpm-lock.yaml
generated
74
docs/pnpm-lock.yaml
generated
@@ -13,10 +13,10 @@ dependencies:
|
||||
version: 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
|
||||
'@docusaurus/preset-classic':
|
||||
specifier: 3.1.1
|
||||
version: 3.1.1(@algolia/client-search@4.22.1)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.2.2)
|
||||
version: 3.1.1(@algolia/client-search@4.22.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.2.2)
|
||||
'@mdx-js/react':
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0(@types/react@18.2.48)(react@18.2.0)
|
||||
version: 3.0.0(@types/react@18.2.69)(react@18.2.0)
|
||||
clsx:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0
|
||||
@@ -1523,7 +1523,7 @@ packages:
|
||||
resolution: {integrity: sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==}
|
||||
dev: false
|
||||
|
||||
/@docsearch/react@3.5.2(@algolia/client-search@4.22.1)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0):
|
||||
/@docsearch/react@3.5.2(@algolia/client-search@4.22.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0):
|
||||
resolution: {integrity: sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==}
|
||||
peerDependencies:
|
||||
'@types/react': '>= 16.8.0 < 19.0.0'
|
||||
@@ -1543,7 +1543,7 @@ packages:
|
||||
'@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.22.1)(search-insights@2.13.0)
|
||||
'@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.22.1)
|
||||
'@docsearch/css': 3.5.2
|
||||
'@types/react': 18.2.48
|
||||
'@types/react': 18.2.69
|
||||
algoliasearch: 4.22.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
@@ -2028,7 +2028,7 @@ packages:
|
||||
- webpack-cli
|
||||
dev: false
|
||||
|
||||
/@docusaurus/preset-classic@3.1.1(@algolia/client-search@4.22.1)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.2.2):
|
||||
/@docusaurus/preset-classic@3.1.1(@algolia/client-search@4.22.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.2.2):
|
||||
resolution: {integrity: sha512-jG4ys/hWYf69iaN/xOmF+3kjs4Nnz1Ay3CjFLDtYa8KdxbmUhArA9HmP26ru5N0wbVWhY+6kmpYhTJpez5wTyg==}
|
||||
engines: {node: '>=18.0'}
|
||||
peerDependencies:
|
||||
@@ -2044,9 +2044,9 @@ packages:
|
||||
'@docusaurus/plugin-google-gtag': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
|
||||
'@docusaurus/plugin-google-tag-manager': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
|
||||
'@docusaurus/plugin-sitemap': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
|
||||
'@docusaurus/theme-classic': 3.1.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
|
||||
'@docusaurus/theme-classic': 3.1.1(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
|
||||
'@docusaurus/theme-common': 3.1.1(@docusaurus/types@3.1.1)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
|
||||
'@docusaurus/theme-search-algolia': 3.1.1(@algolia/client-search@4.22.1)(@docusaurus/types@3.1.1)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.2.2)
|
||||
'@docusaurus/theme-search-algolia': 3.1.1(@algolia/client-search@4.22.1)(@docusaurus/types@3.1.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.2.2)
|
||||
'@docusaurus/types': 3.1.1(react-dom@18.2.0)(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
@@ -2081,7 +2081,7 @@ packages:
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
|
||||
/@docusaurus/theme-classic@3.1.1(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2):
|
||||
/@docusaurus/theme-classic@3.1.1(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2):
|
||||
resolution: {integrity: sha512-GiPE/jbWM8Qv1A14lk6s9fhc0LhPEQ00eIczRO4QL2nAQJZXkjPG6zaVx+1cZxPFWbAsqSjKe2lqkwF3fGkQ7Q==}
|
||||
engines: {node: '>=18.0'}
|
||||
peerDependencies:
|
||||
@@ -2100,7 +2100,7 @@ packages:
|
||||
'@docusaurus/utils': 3.1.1(@docusaurus/types@3.1.1)
|
||||
'@docusaurus/utils-common': 3.1.1(@docusaurus/types@3.1.1)
|
||||
'@docusaurus/utils-validation': 3.1.1(@docusaurus/types@3.1.1)
|
||||
'@mdx-js/react': 3.0.0(@types/react@18.2.48)(react@18.2.0)
|
||||
'@mdx-js/react': 3.0.0(@types/react@18.2.69)(react@18.2.0)
|
||||
clsx: 2.1.0
|
||||
copy-text-to-clipboard: 3.2.0
|
||||
infima: 0.2.0-alpha.43
|
||||
@@ -2179,14 +2179,14 @@ packages:
|
||||
- webpack-cli
|
||||
dev: false
|
||||
|
||||
/@docusaurus/theme-search-algolia@3.1.1(@algolia/client-search@4.22.1)(@docusaurus/types@3.1.1)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.2.2):
|
||||
/@docusaurus/theme-search-algolia@3.1.1(@algolia/client-search@4.22.1)(@docusaurus/types@3.1.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)(typescript@5.2.2):
|
||||
resolution: {integrity: sha512-tBH9VY5EpRctVdaAhT+b1BY8y5dyHVZGFXyCHgTrvcXQy5CV4q7serEX7U3SveNT9zksmchPyct6i1sFDC4Z5g==}
|
||||
engines: {node: '>=18.0'}
|
||||
peerDependencies:
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
dependencies:
|
||||
'@docsearch/react': 3.5.2(@algolia/client-search@4.22.1)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)
|
||||
'@docsearch/react': 3.5.2(@algolia/client-search@4.22.1)(@types/react@18.2.69)(react-dom@18.2.0)(react@18.2.0)(search-insights@2.13.0)
|
||||
'@docusaurus/core': 3.1.1(@docusaurus/types@3.1.1)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
|
||||
'@docusaurus/logger': 3.1.1
|
||||
'@docusaurus/plugin-content-docs': 3.1.1(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
|
||||
@@ -2420,14 +2420,14 @@ packages:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
/@mdx-js/react@3.0.0(@types/react@18.2.48)(react@18.2.0):
|
||||
/@mdx-js/react@3.0.0(@types/react@18.2.69)(react@18.2.0):
|
||||
resolution: {integrity: sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '>=16'
|
||||
react: '>=16'
|
||||
dependencies:
|
||||
'@types/mdx': 2.0.10
|
||||
'@types/react': 18.2.48
|
||||
'@types/react': 18.2.69
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@@ -2852,6 +2852,10 @@ packages:
|
||||
/@types/prop-types@15.7.11:
|
||||
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
||||
|
||||
/@types/prop-types@15.7.12:
|
||||
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
|
||||
dev: false
|
||||
|
||||
/@types/qs@6.9.11:
|
||||
resolution: {integrity: sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==}
|
||||
dev: false
|
||||
@@ -2887,6 +2891,14 @@ packages:
|
||||
'@types/scheduler': 0.16.8
|
||||
csstype: 3.1.3
|
||||
|
||||
/@types/react@18.2.69:
|
||||
resolution: {integrity: sha512-W1HOMUWY/1Yyw0ba5TkCV+oqynRjG7BnteBB+B7JmAK7iw3l2SW+VGOxL+akPweix6jk2NNJtyJKpn4TkpfK3Q==}
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.12
|
||||
'@types/scheduler': 0.16.8
|
||||
csstype: 3.1.3
|
||||
dev: false
|
||||
|
||||
/@types/retry@0.12.0:
|
||||
resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
|
||||
dev: false
|
||||
@@ -3341,8 +3353,8 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/body-parser@1.20.1:
|
||||
resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
|
||||
/body-parser@1.20.2:
|
||||
resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
@@ -3354,7 +3366,7 @@ packages:
|
||||
iconv-lite: 0.4.24
|
||||
on-finished: 2.4.1
|
||||
qs: 6.11.0
|
||||
raw-body: 2.5.1
|
||||
raw-body: 2.5.2
|
||||
type-is: 1.6.18
|
||||
unpipe: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
@@ -3776,8 +3788,8 @@ packages:
|
||||
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
|
||||
dev: false
|
||||
|
||||
/cookie@0.5.0:
|
||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
||||
/cookie@0.6.0:
|
||||
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
@@ -4508,16 +4520,16 @@ packages:
|
||||
strip-final-newline: 2.0.0
|
||||
dev: false
|
||||
|
||||
/express@4.18.2:
|
||||
resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==}
|
||||
/express@4.19.2:
|
||||
resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
dependencies:
|
||||
accepts: 1.3.8
|
||||
array-flatten: 1.1.1
|
||||
body-parser: 1.20.1
|
||||
body-parser: 1.20.2
|
||||
content-disposition: 0.5.4
|
||||
content-type: 1.0.5
|
||||
cookie: 0.5.0
|
||||
cookie: 0.6.0
|
||||
cookie-signature: 1.0.6
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
@@ -4679,8 +4691,8 @@ packages:
|
||||
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
|
||||
hasBin: true
|
||||
|
||||
/follow-redirects@1.15.5:
|
||||
resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==}
|
||||
/follow-redirects@1.15.6:
|
||||
resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
@@ -5268,7 +5280,7 @@ packages:
|
||||
engines: {node: '>=8.0.0'}
|
||||
dependencies:
|
||||
eventemitter3: 4.0.7
|
||||
follow-redirects: 1.15.5
|
||||
follow-redirects: 1.15.6
|
||||
requires-port: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
@@ -7419,8 +7431,8 @@ packages:
|
||||
engines: {node: '>= 0.6'}
|
||||
dev: false
|
||||
|
||||
/raw-body@2.5.1:
|
||||
resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
|
||||
/raw-body@2.5.2:
|
||||
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
|
||||
engines: {node: '>= 0.8'}
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
@@ -8783,8 +8795,8 @@ packages:
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/webpack-dev-middleware@5.3.3(webpack@5.90.0):
|
||||
resolution: {integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==}
|
||||
/webpack-dev-middleware@5.3.4(webpack@5.90.0):
|
||||
resolution: {integrity: sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==}
|
||||
engines: {node: '>= 12.13.0'}
|
||||
peerDependencies:
|
||||
webpack: ^4.0.0 || ^5.0.0
|
||||
@@ -8824,7 +8836,7 @@ packages:
|
||||
compression: 1.7.4
|
||||
connect-history-api-fallback: 2.0.0
|
||||
default-gateway: 6.0.3
|
||||
express: 4.18.2
|
||||
express: 4.19.2
|
||||
graceful-fs: 4.2.11
|
||||
html-entities: 2.4.0
|
||||
http-proxy-middleware: 2.0.6(@types/express@4.17.21)
|
||||
@@ -8839,7 +8851,7 @@ packages:
|
||||
sockjs: 0.3.24
|
||||
spdy: 4.0.2
|
||||
webpack: 5.90.0
|
||||
webpack-dev-middleware: 5.3.3(webpack@5.90.0)
|
||||
webpack-dev-middleware: 5.3.4(webpack@5.90.0)
|
||||
ws: 8.16.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react"
|
||||
import * as css from "./{{pascalCae name}}.css"
|
||||
import * as css from "./{{pascalCase name}}.css"
|
||||
|
||||
class {{pascalCae name}} extends React.Component<any> {
|
||||
class {{pascalCase name}} extends React.Component<any> {
|
||||
private {{ property }}
|
||||
|
||||
constructor(props: any) {
|
||||
@@ -10,8 +10,8 @@ class {{pascalCae name}} extends React.Component<any> {
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <div className={ css.{{pascalCae name}} } />
|
||||
return <div className={ css.{{pascalCase name}} } />
|
||||
}
|
||||
}
|
||||
|
||||
export default {pascalCae nName}}
|
||||
export default {{pascalCase name}}
|
||||
|
||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-scaffold",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.1",
|
||||
"description": "Generate any file structure - from single components to entire app boilerplates, with a single command.",
|
||||
"homepage": "https: //chenasraf.github.io/simple-scaffold",
|
||||
"repository": {
|
||||
@@ -13,7 +13,7 @@
|
||||
"bin": {
|
||||
"simple-scaffold": "cmd.js"
|
||||
},
|
||||
"packageManager": "pnpm@8.15.1",
|
||||
"packageManager": "pnpm@9.0.4",
|
||||
"keywords": [
|
||||
"javascript",
|
||||
"cli",
|
||||
@@ -39,8 +39,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
"date-fns": "^3.3.1",
|
||||
"glob": "^10.3.10",
|
||||
"date-fns": "^3.6.0",
|
||||
"glob": "^10.3.12",
|
||||
"handlebars": "^4.7.8",
|
||||
"massarg": "2.0.0"
|
||||
},
|
||||
@@ -51,16 +51,16 @@
|
||||
"@semantic-release/release-notes-generator": "^12.1.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/mock-fs": "^4.13.4",
|
||||
"@types/node": "^20.11.17",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/semantic-release": "^20.0.6",
|
||||
"conventional-changelog": "^5.1.0",
|
||||
"conventional-changelog-cli": "^4.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"mock-fs": "^5.2.0",
|
||||
"semantic-release": "^23.0.2",
|
||||
"semantic-release": "^23.0.8",
|
||||
"semantic-release-conventional-commits": "^3.0.0",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
}
|
||||
|
||||
7050
pnpm-lock.yaml
generated
7050
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -13,31 +13,27 @@ module.exports = {
|
||||
"@semantic-release/npm",
|
||||
{
|
||||
// only update the pkg version on root, don't publish
|
||||
// this is to keep package.json version in sync with the release
|
||||
npmPublish: false,
|
||||
},
|
||||
],
|
||||
// [
|
||||
// '@semantic-release/npm',
|
||||
// {
|
||||
// // only update the pkg version on doc, don't publish
|
||||
// npmPublish: false,
|
||||
// pkgRoot: 'doc',
|
||||
// },
|
||||
// ]
|
||||
[
|
||||
"@semantic-release/exec",
|
||||
{
|
||||
publish: "cd ./dist && pnpm pack --pack-destination=../",
|
||||
// pack the dist folder, during publish step (after version was bumped)
|
||||
publishCmd: 'echo "Packing..."; cd ./dist && pnpm pack --pack-destination=../; echo "Done"',
|
||||
},
|
||||
],
|
||||
[
|
||||
"@semantic-release/npm",
|
||||
{
|
||||
// publish from dist dir instead of root
|
||||
// this is the actual uild output
|
||||
pkgRoot: "dist",
|
||||
},
|
||||
],
|
||||
[
|
||||
// Release to GitHub
|
||||
"@semantic-release/github",
|
||||
{
|
||||
assets: ["*.tgz"],
|
||||
@@ -45,14 +41,16 @@ module.exports = {
|
||||
],
|
||||
branch === "master"
|
||||
? [
|
||||
"@semantic-release/changelog",
|
||||
{
|
||||
changelogFile: "CHANGELOG.md",
|
||||
changelogTitle: "# Change Log",
|
||||
},
|
||||
]
|
||||
// Update CHANGELOG.md only on master
|
||||
"@semantic-release/changelog",
|
||||
{
|
||||
changelogFile: "CHANGELOG.md",
|
||||
changelogTitle: "# Change Log",
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
[
|
||||
// Commit the package.json and CHANGELOG.md files to git (if modified)
|
||||
"@semantic-release/git",
|
||||
{
|
||||
assets: ["package.json", "CHANGELOG.md"].filter(Boolean),
|
||||
|
||||
101
src/cmd.ts
101
src/cmd.ts
@@ -1,22 +1,24 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import os from "node:os"
|
||||
import { massarg } from "massarg"
|
||||
import chalk from "chalk"
|
||||
import { LogLevel, ScaffoldCmdConfig } from "./types"
|
||||
import { Scaffold } from "./scaffold"
|
||||
import path from "node:path"
|
||||
import fs from "node:fs/promises"
|
||||
import { parseAppendData, parseConfigFile } from "./config"
|
||||
import { massarg } from "massarg"
|
||||
import chalk from "chalk"
|
||||
import { ListCommandCliOptions, LogLevel, ScaffoldCmdConfig } from "./types"
|
||||
import { Scaffold } from "./scaffold"
|
||||
import { getConfigFile, parseAppendData, parseConfigFile } from "./config"
|
||||
import { log } from "./logger"
|
||||
import { MassargCommand } from "massarg/command"
|
||||
import { getUniqueTmpPath as generateUniqueTmpPath } from "./file"
|
||||
|
||||
export async function parseCliArgs(args = process.argv.slice(2)) {
|
||||
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 isVersionFlag = args.includes("--version") || args.includes("-v")
|
||||
const isConfigProvided =
|
||||
args.includes("--config") || args.includes("-c") || args.includes("--git") || args.includes("-g") || isVersionFlag
|
||||
const isConfigFileProvided = args.includes("--config") || args.includes("-c")
|
||||
const isGitProvided = args.includes("--git") || args.includes("-g")
|
||||
const isConfigProvided = isConfigFileProvided || isGitProvided || isVersionFlag
|
||||
|
||||
return massarg<ScaffoldCmdConfig>({
|
||||
name: pkg.name,
|
||||
@@ -28,7 +30,7 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
|
||||
return
|
||||
}
|
||||
log(config, LogLevel.info, `Simple Scaffold v${pkg.version}`)
|
||||
const tmpPath = path.resolve(os.tmpdir(), `scaffold-config-${Date.now()}`)
|
||||
const tmpPath = generateUniqueTmpPath()
|
||||
try {
|
||||
log(config, LogLevel.debug, "Parsing config file...", config)
|
||||
const parsed = await parseConfigFile(config, tmpPath)
|
||||
@@ -46,24 +48,20 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
|
||||
aliases: ["n"],
|
||||
description:
|
||||
"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.",
|
||||
"contents and file names will be replaced accordingly. You may omit the `--name` or `-n` " +
|
||||
"for this specific option.",
|
||||
isDefault: true,
|
||||
required: !isConfigProvided,
|
||||
})
|
||||
.option({
|
||||
name: "config",
|
||||
aliases: ["c"],
|
||||
description:
|
||||
"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.",
|
||||
description: "Filename or directory to load config from",
|
||||
})
|
||||
.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.",
|
||||
description: "Git URL or GitHub path to load a template from.",
|
||||
})
|
||||
.option({
|
||||
name: "key",
|
||||
@@ -146,6 +144,14 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
|
||||
return val
|
||||
},
|
||||
})
|
||||
.option({
|
||||
name: "before-write",
|
||||
aliases: ["B"],
|
||||
description:
|
||||
"Run a script before writing the files. This can be a command or a path to a" +
|
||||
" file. A temporary file path will be passed to the given command and the command should " +
|
||||
"return a string for the final output.",
|
||||
})
|
||||
.flag({
|
||||
name: "dry-run",
|
||||
aliases: ["dr"],
|
||||
@@ -159,6 +165,67 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
|
||||
aliases: ["v"],
|
||||
description: "Display version.",
|
||||
})
|
||||
.command(
|
||||
new MassargCommand<ListCommandCliOptions>({
|
||||
name: "list",
|
||||
aliases: ["ls"],
|
||||
description: "List all available templates for a given config. See `list -h` for more information.",
|
||||
run: async (_config) => {
|
||||
const tmpPath = generateUniqueTmpPath()
|
||||
const config = {
|
||||
templates: [],
|
||||
name: "",
|
||||
version: false,
|
||||
output: "",
|
||||
subdir: false,
|
||||
overwrite: false,
|
||||
dryRun: false,
|
||||
..._config,
|
||||
config: _config.config ?? (!_config.git ? process.cwd() : undefined),
|
||||
}
|
||||
try {
|
||||
const file = await getConfigFile(config, tmpPath)
|
||||
console.log(chalk.underline`Available templates:\n`)
|
||||
console.log(Object.keys(file).join("\n"))
|
||||
} catch (e) {
|
||||
const message = "message" in (e as any) ? (e as any).message : e?.toString()
|
||||
log(config, LogLevel.error, message)
|
||||
} finally {
|
||||
log(config, LogLevel.debug, "Cleaning up temporary files...", tmpPath)
|
||||
await fs.rm(tmpPath, { recursive: true, force: true })
|
||||
}
|
||||
},
|
||||
})
|
||||
.option({
|
||||
name: "config",
|
||||
aliases: ["c"],
|
||||
description: "Filename or directory to load config from. Defaults to current working directory.",
|
||||
})
|
||||
.option({
|
||||
name: "git",
|
||||
aliases: ["g"],
|
||||
description: "Git URL or GitHub path to load a template from.",
|
||||
})
|
||||
.option({
|
||||
name: "log-level",
|
||||
aliases: ["l"],
|
||||
defaultValue: LogLevel.none,
|
||||
description:
|
||||
"Determine amount of logs to display. The values are: " +
|
||||
`${chalk.bold`\`none | debug | info | warn | error\``}. ` +
|
||||
"The provided level will display messages of the same level or higher.",
|
||||
parse: (v) => {
|
||||
const val = v.toLowerCase()
|
||||
if (!(val in LogLevel)) {
|
||||
throw new Error(`Invalid log level: ${val}, must be one of: ${Object.keys(LogLevel).join(", ")}`)
|
||||
}
|
||||
return val
|
||||
},
|
||||
})
|
||||
.help({
|
||||
bindOption: true,
|
||||
}),
|
||||
)
|
||||
.example({
|
||||
description: "Usage with config file",
|
||||
input: "simple-scaffold -c scaffold.cmd.js --key component",
|
||||
|
||||
138
src/config.ts
138
src/config.ts
@@ -10,12 +10,14 @@ import {
|
||||
ScaffoldCmdConfig,
|
||||
ScaffoldConfig,
|
||||
ScaffoldConfigFile,
|
||||
ScaffoldConfigMap,
|
||||
} from "./types"
|
||||
import { handlebarsParse } from "./parser"
|
||||
import { log } from "./logger"
|
||||
import { resolve, wrapNoopResolver } from "./utils"
|
||||
import { getGitConfig } from "./git"
|
||||
import { isDir, pathExists } from "./file"
|
||||
import { createDirIfNotExists, getUniqueTmpPath, isDir, pathExists } from "./file"
|
||||
import { exec, spawn } from "node:child_process"
|
||||
|
||||
/** @internal */
|
||||
export function getOptionValueForFile<T>(
|
||||
@@ -50,43 +52,49 @@ function isWrappedWithQuotes(string: string): boolean {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function parseConfigFile(config: ScaffoldCmdConfig, tmpPath: string): Promise<ScaffoldConfig> {
|
||||
let output: ScaffoldConfig = config
|
||||
|
||||
if (config.quiet) {
|
||||
config.logLevel = LogLevel.none
|
||||
}
|
||||
|
||||
export async function getConfigFile(config: ScaffoldCmdConfig, tmpPath: string): Promise<ScaffoldConfigMap> {
|
||||
if (config.git && !config.git.includes("://")) {
|
||||
log(config, LogLevel.info, `Loading config from GitHub ${config.git}`)
|
||||
config.git = githubPartToUrl(config.git)
|
||||
}
|
||||
|
||||
const shouldLoadConfig = config.config || config.git
|
||||
const isGit = Boolean(config.git)
|
||||
const configFilename = config.config
|
||||
const configPath = isGit ? config.git : configFilename
|
||||
|
||||
log(config, LogLevel.info, `Loading config from file ${configFilename}`)
|
||||
|
||||
const configPromise = await (isGit
|
||||
? getRemoteConfig({ git: configPath, config: configFilename, logLevel: config.logLevel, tmpPath })
|
||||
: getLocalConfig({ config: configFilename, logLevel: config.logLevel }))
|
||||
|
||||
// resolve the config
|
||||
let configImport = await resolve(configPromise, config)
|
||||
|
||||
// If the config is a function or promise, return the output
|
||||
if (typeof configImport.default === "function" || configImport.default instanceof Promise) {
|
||||
log(config, LogLevel.debug, "Config is a function or promise, resolving...")
|
||||
configImport = await resolve(configImport.default, config)
|
||||
}
|
||||
return configImport
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export async function parseConfigFile(config: ScaffoldCmdConfig, tmpPath: string): Promise<ScaffoldConfig> {
|
||||
let output: ScaffoldConfig = { ...config, beforeWrite: undefined }
|
||||
|
||||
if (config.quiet) {
|
||||
config.logLevel = LogLevel.none
|
||||
}
|
||||
|
||||
const shouldLoadConfig = Boolean(config.config || config.git)
|
||||
|
||||
if (shouldLoadConfig) {
|
||||
const isGit = Boolean(config.git)
|
||||
const key = config.key ?? "default"
|
||||
const configFilename = config.config
|
||||
const configPath = isGit ? config.git : configFilename
|
||||
|
||||
log(config, LogLevel.info, `Loading config from file ${configFilename} with key ${key}`)
|
||||
|
||||
const configPromise = await (isGit
|
||||
? getRemoteConfig({ git: configPath, config: configFilename, logLevel: config.logLevel, tmpPath })
|
||||
: getLocalConfig({ config: configFilename, logLevel: config.logLevel }))
|
||||
|
||||
// resolve the config
|
||||
let configImport = await resolve(configPromise, config)
|
||||
|
||||
// If the config is a function or promise, return the output
|
||||
if (typeof configImport.default === "function" || configImport.default instanceof Promise) {
|
||||
log(config, LogLevel.debug, "Config is a function or promise, resolving...")
|
||||
configImport = await resolve(configImport.default, config)
|
||||
}
|
||||
const configImport = await getConfigFile(config, tmpPath)
|
||||
|
||||
if (!configImport[key]) {
|
||||
throw new Error(`Template "${key}" not found in ${configFilename}`)
|
||||
throw new Error(`Template "${key}" not found in ${config.config}`)
|
||||
}
|
||||
|
||||
const imported = configImport[key]
|
||||
@@ -94,6 +102,7 @@ export async function parseConfigFile(config: ScaffoldCmdConfig, tmpPath: string
|
||||
output = {
|
||||
...config,
|
||||
...imported,
|
||||
beforeWrite: undefined,
|
||||
data: {
|
||||
...(imported as any).data,
|
||||
...config.data,
|
||||
@@ -102,9 +111,13 @@ export async function parseConfigFile(config: ScaffoldCmdConfig, tmpPath: string
|
||||
}
|
||||
|
||||
output.data = { ...output.data, ...config.appendData }
|
||||
const cmdBeforeWrite = config.beforeWrite ? wrapBeforeWrite(config, config.beforeWrite) : undefined
|
||||
output.beforeWrite = cmdBeforeWrite ?? output.beforeWrite
|
||||
|
||||
if (!output.name) {
|
||||
throw new Error("simple-scaffold: Missing required option: name")
|
||||
}
|
||||
|
||||
log(output, LogLevel.debug, "Parsed config", output)
|
||||
return output
|
||||
}
|
||||
@@ -175,3 +188,72 @@ export async function findConfigFile(root: string): Promise<string> {
|
||||
}
|
||||
throw new Error(`Could not find config file in git repo`)
|
||||
}
|
||||
|
||||
function wrapBeforeWrite(
|
||||
config: LogConfig & Pick<ScaffoldConfig, "dryRun">,
|
||||
beforeWrite: string,
|
||||
): ScaffoldConfig["beforeWrite"] {
|
||||
return async (content, rawContent, outputFile) => {
|
||||
const tmpPath = path.join(getUniqueTmpPath(), path.basename(outputFile))
|
||||
await createDirIfNotExists(path.dirname(tmpPath), config)
|
||||
const ext = path.extname(outputFile)
|
||||
const rawTmpPath = tmpPath.replace(ext, ".raw" + ext)
|
||||
try {
|
||||
log(config, LogLevel.debug, "Parsing beforeWrite command", beforeWrite)
|
||||
let cmd = await prepareBeforeWriteCmd({ beforeWrite, tmpPath, content, rawTmpPath, rawContent })
|
||||
const result = await new Promise<string | undefined>((resolve, reject) => {
|
||||
log(config, LogLevel.debug, "Running parsed beforeWrite command:", cmd)
|
||||
const proc = exec(cmd)
|
||||
proc.stdout!.on("data", (data) => {
|
||||
if (data.trim()) {
|
||||
resolve(data.toString())
|
||||
} else {
|
||||
resolve(undefined)
|
||||
}
|
||||
})
|
||||
proc.stderr!.on("data", (data) => {
|
||||
reject(data.toString())
|
||||
})
|
||||
})
|
||||
return result
|
||||
} catch (e) {
|
||||
log(config, LogLevel.debug, e)
|
||||
log(config, LogLevel.warning, "Error running beforeWrite command, returning original content")
|
||||
return undefined
|
||||
} finally {
|
||||
await fs.rm(tmpPath, { force: true })
|
||||
await fs.rm(rawTmpPath, { force: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function prepareBeforeWriteCmd({
|
||||
beforeWrite,
|
||||
tmpPath,
|
||||
content,
|
||||
rawTmpPath,
|
||||
rawContent,
|
||||
}: {
|
||||
beforeWrite: string
|
||||
tmpPath: string
|
||||
content: Buffer
|
||||
rawTmpPath: string
|
||||
rawContent: Buffer
|
||||
}): Promise<string> {
|
||||
let cmd: string = ""
|
||||
const pathReg = /\{\{\s*path\s*\}\}/gi
|
||||
const rawPathReg = /\{\{\s*rawpath\s*\}\}/gi
|
||||
if (pathReg.test(beforeWrite)) {
|
||||
await fs.writeFile(tmpPath, content)
|
||||
cmd = beforeWrite.replaceAll(pathReg, tmpPath)
|
||||
}
|
||||
if (rawPathReg.test(beforeWrite)) {
|
||||
await fs.writeFile(rawTmpPath, rawContent)
|
||||
cmd = beforeWrite.replaceAll(rawPathReg, rawTmpPath)
|
||||
}
|
||||
if (!cmd) {
|
||||
await fs.writeFile(tmpPath, content)
|
||||
cmd = [beforeWrite, tmpPath].join(" ")
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
18
src/file.ts
18
src/file.ts
@@ -1,7 +1,8 @@
|
||||
import os from "node:os"
|
||||
import path from "node:path"
|
||||
import { F_OK } from "node:constants"
|
||||
import { LogLevel, ScaffoldConfig } from "./types"
|
||||
import fs from "node:fs/promises"
|
||||
import { F_OK } from "node:constants"
|
||||
import { LogConfig, LogLevel, ScaffoldConfig } from "./types"
|
||||
import { glob, hasMagic } from "glob"
|
||||
import { log } from "./logger"
|
||||
import { getOptionValueForFile } from "./config"
|
||||
@@ -10,7 +11,10 @@ import { handleErr } from "./utils"
|
||||
|
||||
const { stat, access, mkdir, readFile, writeFile } = fs
|
||||
|
||||
export async function createDirIfNotExists(dir: string, config: ScaffoldConfig): Promise<void> {
|
||||
export async function createDirIfNotExists(
|
||||
dir: string,
|
||||
config: LogConfig & Pick<ScaffoldConfig, "dryRun">,
|
||||
): Promise<void> {
|
||||
if (config.dryRun) {
|
||||
log(config, LogLevel.info, `Dry Run. Not creating dir ${dir}`)
|
||||
return
|
||||
@@ -142,6 +146,7 @@ export async function copyFileTransformed(
|
||||
if (exists && overwrite) {
|
||||
log(config, LogLevel.info, `File ${outputPath} exists, overwriting`)
|
||||
}
|
||||
log(config, LogLevel.debug, `Processing file ${inputPath}`)
|
||||
const templateBuffer = await readFile(inputPath)
|
||||
const unprocessedOutputContents = handlebarsParse(config, templateBuffer)
|
||||
const finalOutputContents =
|
||||
@@ -149,7 +154,6 @@ export async function copyFileTransformed(
|
||||
|
||||
if (!config.dryRun) {
|
||||
await writeFile(outputPath, finalOutputContents)
|
||||
log(config, LogLevel.info, "Done.")
|
||||
} else {
|
||||
log(config, LogLevel.info, "Dry Run. Output should be:")
|
||||
log(config, LogLevel.info, finalOutputContents.toString())
|
||||
@@ -157,6 +161,7 @@ export async function copyFileTransformed(
|
||||
} else if (exists) {
|
||||
log(config, LogLevel.info, `File ${outputPath} already exists, skipping`)
|
||||
}
|
||||
log(config, LogLevel.info, "Done.")
|
||||
}
|
||||
|
||||
export function getOutputDir(config: ScaffoldConfig, outputPathOpt: string, basePath: string): string {
|
||||
@@ -209,3 +214,8 @@ export async function handleTemplateFile(
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function getUniqueTmpPath(): string {
|
||||
return path.resolve(os.tmpdir(), `scaffold-config-${Date.now()}-${Math.random().toString(36).slice(2)}`)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import util from "util"
|
||||
import { LogConfig, LogLevel, ScaffoldConfig } from "./types"
|
||||
import chalk from "chalk"
|
||||
|
||||
@@ -30,7 +31,7 @@ export function log(config: LogConfig, level: LogLevel, ...obj: any[]): void {
|
||||
i instanceof Error
|
||||
? chalkFn(i, JSON.stringify(i, undefined, 1), i.stack)
|
||||
: typeof i === "object"
|
||||
? chalkFn(JSON.stringify(i, undefined, 1))
|
||||
? util.inspect(i, { depth: null, colors: true })
|
||||
: chalkFn(i),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -368,6 +368,8 @@ export type ScaffoldCmdConfig = {
|
||||
git?: string
|
||||
/** Display version */
|
||||
version: boolean
|
||||
/** Run a script before writing the files. This can be a command or a path to a file. The file contents will be passed to the given command. */
|
||||
beforeWrite?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,3 +415,5 @@ export type RemoteConfigLoadConfig = LogConfig & Pick<ScaffoldCmdConfig, "config
|
||||
|
||||
/** @internal */
|
||||
export type MinimalConfig = Pick<ScaffoldCmdConfig, "name" | "key">
|
||||
|
||||
export type ListCommandCliOptions = Pick<ScaffoldCmdConfig, "config" | "git" | "logLevel" | "quiet">
|
||||
|
||||
Reference in New Issue
Block a user