Compare commits

...

39 Commits

Author SHA1 Message Date
semantic-release-bot
7be79fdcaf chore(release): 1.5.0 [skip ci]
## [1.5.0](https://github.com/chenasraf/simple-scaffold/compare/v1.4.0...v1.5.0) (2023-05-02)

### Features

* add github remote templates ([f961c13](f961c13da1))
* support for remote template configs ([05487f4](05487f4d1e))
2023-05-02 07:28:17 +00:00
Chen Asraf
6afbcdc258 Merge pull request #55 from chenasraf/develop
v1.5.0
2023-05-02 10:27:11 +03:00
Chen Asraf
1a3fd3d610 docs: update help text 2023-05-02 10:23:50 +03:00
Chen Asraf
7f10db0d6e chore: bump version number 2023-05-02 10:16:47 +03:00
semantic-release-bot
93f5b4a004 chore(release): 1.5.0-develop.1 [skip ci]
## [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](f961c13da1))
* support for remote template configs ([05487f4](05487f4d1e))
2023-05-02 07:12:11 +00:00
Chen Asraf
b74411ddb8 docs: add docs for remote templates 2023-05-02 10:10:50 +03:00
Chen Asraf
f961c13da1 feat: add github remote templates 2023-05-02 09:46:53 +03:00
Chen Asraf
05487f4d1e feat: support for remote template configs 2023-05-02 09:33:54 +03:00
Chen Asraf
c50518a19c build: fix git/github step order 2023-04-29 17:17:15 +03:00
Chen Asraf
10ea6b4132 chore: fix package version 2023-04-29 17:11:18 +03:00
Chen Asraf
ce399181ab build: always build docs 2023-04-29 17:05:50 +03:00
semantic-release-bot
83d38073f3 chore(release): 1.4.0 [skip ci]
## [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](6c5ba0bc91))
2023-04-28 23:01:48 +00:00
Chen Asraf
6c5ba0bc91 feat: add --key | -k to config loader 2023-04-29 01:59:39 +03:00
semantic-release-bot
2c4eccd800 chore(release): 1.3.2 [skip ci]
## [1.3.2](https://github.com/chenasraf/simple-scaffold/compare/v1.3.1...v1.3.2) (2023-04-28)

### Bug Fixes

* release build ([2c23fa9](2c23fa9dbb))
* release build asset ([0bef2df](0bef2df5f3))
2023-04-28 22:08:51 +00:00
Chen Asraf
2c23fa9dbb fix: release build 2023-04-29 01:04:01 +03:00
Chen Asraf
0bef2df5f3 fix: release build asset 2023-04-29 00:59:54 +03:00
semantic-release-bot
398a5d723b chore(release): 1.3.1 [skip ci]
## [1.3.1](https://github.com/chenasraf/simple-scaffold/compare/v1.3.0...v1.3.1) (2023-04-28)

### Bug Fixes

* docs ([6e19a86](6e19a86190))
* remove old peer-dep ([c7e2ef8](c7e2ef862c))
2023-04-28 21:50:36 +00:00
Chen Asraf
c7e2ef862c fix: remove old peer-dep 2023-04-29 00:44:49 +03:00
Chen Asraf
36dd27e480 docs: fix doc links 2023-04-29 00:44:49 +03:00
Chen Asraf
6e19a86190 fix: docs 2023-04-29 00:44:49 +03:00
Chen Asraf
eba7897f84 chore: bump version number + fix changelog 2023-04-29 00:44:38 +03:00
semantic-release-bot
dfdbca59a7 chore(release): 1.3.0 [skip ci]
## [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](c3989769fe))

### Bug Fixes

* config option should not be mandatory ([3db6a91](3db6a918f1))
* export config file type ([4302eb5](4302eb5ce3))
2023-04-25 07:27:13 +00:00
Chen Asraf
1d6643b7eb docs: clean up css 2023-04-25 10:26:24 +03:00
Chen Asraf
9b86499a04 docs: use js for typedoc config 2023-04-25 10:26:24 +03:00
Chen Asraf
f41ebfb1d0 docs: reorganize file structure 2023-04-25 10:26:24 +03:00
Chen Asraf
873fa77c9a docs: add changelog to typedoc 2023-04-25 10:26:24 +03:00
Chen Asraf
1ce4a41a97 build: update changelog sections 2023-04-25 10:26:24 +03:00
Chen Asraf
4302eb5ce3 fix: export config file type 2023-04-25 10:26:24 +03:00
Chen Asraf
146086869f docs: move migration to pages, fix urls 2023-04-25 10:26:24 +03:00
Chen Asraf
8a2207bec5 docs: add table of contents 2023-04-25 10:26:24 +03:00
Chen Asraf
42568e0fa1 docs: remove unnecessary nested menus 2023-04-25 10:26:24 +03:00
Chen Asraf
a4498f9539 docs: split into files 2023-04-25 10:26:24 +03:00
Chen Asraf
5a2b187115 docs: update config doc 2023-04-25 10:26:24 +03:00
Chen Asraf
3db6a918f1 fix: config option should not be mandatory 2023-04-25 10:26:24 +03:00
Chen Asraf
c3989769fe feat: load scaffold config from files 2023-04-25 10:26:24 +03:00
Chen Asraf
b3f7912760 build: remove unnecessary yarn pack 2023-04-25 10:26:24 +03:00
Chen Asraf
1e0b73190c build: only generate docs on master 2023-04-25 10:26:24 +03:00
Chen Asraf
0c8e6e7573 docs: fix CHANGELOG.md 2023-04-25 10:26:24 +03:00
Chen Asraf
f28280e815 docs: update readme 2023-04-25 10:26:24 +03:00
21 changed files with 1047 additions and 476 deletions

View File

@@ -16,7 +16,7 @@ Steps to reproduce the behavior:
1. Prepare templates:
```txt
```text
This is my {{ template }}
```

View File

@@ -2,18 +2,19 @@ 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
with:
node-version: "18.x"
- run: yarn install --frozen-lockfile
- run: yarn typedoc
- run: yarn build-docs
- uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -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
@@ -16,7 +16,6 @@ jobs:
- run: yarn test
- run: yarn build
- run: cd ./dist && yarn pack --filename=../package.tgz
if: "!contains(github.event.head_commit.message, '[skip publish]')"
- run: yarn semantic-release
if: "!contains(github.event.head_commit.message, '[skip publish]')"
env:

View File

@@ -1,25 +1,75 @@
# Change Log
## [1.2.0](https://github.com/chenasraf/simple-scaffold/compare/v1.1.4...v1.2.0) (2023-04-24)
## [1.5.0](https://github.com/chenasraf/simple-scaffold/compare/v1.4.0...v1.5.0) (2023-05-02)
### Features
* append-data cli flag ([3c5c2de](https://github.com/chenasraf/simple-scaffold/commit/3c5c2ded02f61ff086e81ea4a7f40529bdff1c9d))
* 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
* ci node version ([767d34c](https://github.com/chenasraf/simple-scaffold/commit/767d34c684516d4cea865b25e87c27c779bb79ce))
* semantic-release build dir ([f7956dd](https://github.com/chenasraf/simple-scaffold/commit/f7956ddc786018905c48ccf1f21a3bb4657c3d75))
* support quote wrapping in append-data ([4fecca8](https://github.com/chenasraf/simple-scaffold/commit/4fecca848347312d45d704f82f2bcb3822da9b06))
* release build ([2c23fa9](https://github.com/chenasraf/simple-scaffold/commit/2c23fa9dbb310cd0a31f09606798f96b95d66779))
* release build asset ([0bef2df](https://github.com/chenasraf/simple-scaffold/commit/0bef2df5f3aa800ad5f1094c0996108db9acce51))
### [1.1.4](https://github.com/chenasraf/simple-scaffold/compare/v1.1.3...v1.1.4) (2023-04-24)
## [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)
### Features
- append-data cli flag
([2f4f7fc](https://github.com/chenasraf/simple-scaffold/commit/2f4f7fce47669e37ff49154fbec2acdfd0f5860e))
([3c5c2de](https://github.com/chenasraf/simple-scaffold/commit/3c5c2ded02f61ff086e81ea4a7f40529bdff1c9d))
### Bug Fixes
- ci node version
([767d34c](https://github.com/chenasraf/simple-scaffold/commit/767d34c684516d4cea865b25e87c27c779bb79ce))
- semantic-release build dir
([f7956dd](https://github.com/chenasraf/simple-scaffold/commit/f7956ddc786018905c48ccf1f21a3bb4657c3d75))
- support quote wrapping in append-data
([4fecca8](https://github.com/chenasraf/simple-scaffold/commit/4fecca848347312d45d704f82f2bcb3822da9b06))
## [1.1.4](https://github.com/chenasraf/simple-scaffold/compare/v1.1.3...v1.1.4) (2023-03-13)
### Bug Fixes
@@ -28,7 +78,16 @@
- github action node version
([94fec76](https://github.com/chenasraf/simple-scaffold/commit/94fec766165f7540c578dbf2d0aeeb6ea3969ad8))
### [1.1.3](https://github.com/chenasraf/simple-scaffold/compare/v1.1.2...v1.1.3) (2023-03-11)
### Misc
- update typedoc version
([c334396](https://github.com/chenasraf/simple-scaffold/commit/c334396d74414cbe0aba305c66dfad7fdeb88669))
- update dependencies
([20400bd](https://github.com/chenasraf/simple-scaffold/commit/20400bd81dd43d457427675286c9964a8bc0d5f6))
- bump version number
([8e432bf](https://github.com/chenasraf/simple-scaffold/commit/8e432bfb0b410dc0655c3924031bea2648a42ad0))
## [1.1.3](https://github.com/chenasraf/simple-scaffold/compare/v1.1.2...v1.1.3) (2023-03-11)
### Bug Fixes
@@ -39,19 +98,19 @@
## [1.1.0](https://github.com/chenasraf/simple-scaffold/compare/v1.0.4...v1.1.0) (2022-04-21)
### [1.0.3](https://github.com/chenasraf/simple-scaffold/compare/v1.0.2...v1.0.3) (2022-03-03)
## [1.0.3](https://github.com/chenasraf/simple-scaffold/compare/v1.0.2...v1.0.3) (2022-03-03)
### [1.0.1-pre.1](https://github.com/chenasraf/simple-scaffold/compare/v1.0.0...v1.0.1-pre.1) (2022-02-17)
## [1.0.1-pre.1](https://github.com/chenasraf/simple-scaffold/compare/v1.0.0...v1.0.1-pre.1) (2022-02-17)
### [0.7.5](https://github.com/chenasraf/simple-scaffold/compare/v0.7.4...v0.7.5) (2021-09-26)
## [0.7.5](https://github.com/chenasraf/simple-scaffold/compare/v0.7.4...v0.7.5) (2021-09-26)
### [0.7.4](https://github.com/chenasraf/simple-scaffold/compare/v0.7.3...v0.7.4) (2021-09-26)
## [0.7.4](https://github.com/chenasraf/simple-scaffold/compare/v0.7.3...v0.7.4) (2021-09-26)
### [0.7.3](https://github.com/chenasraf/simple-scaffold/compare/v0.7.2...v0.7.3) (2021-09-26)
## [0.7.3](https://github.com/chenasraf/simple-scaffold/compare/v0.7.2...v0.7.3) (2021-09-26)
### [0.7.2](https://github.com/chenasraf/simple-scaffold/compare/v0.6.1...v0.7.2) (2021-04-19)
## [0.7.2](https://github.com/chenasraf/simple-scaffold/compare/v0.6.1...v0.7.2) (2021-04-19)
### [0.6.1](https://github.com/chenasraf/simple-scaffold/compare/v0.6.0...v0.6.1) (2021-02-01)
## [0.6.1](https://github.com/chenasraf/simple-scaffold/compare/v0.6.0...v0.6.1) (2021-02-01)
### Bug Fixes
@@ -67,26 +126,26 @@
## [0.5.0](https://github.com/chenasraf/simple-scaffold/compare/v0.4.5...v0.5.0) (2019-02-27)
### [0.4.5](https://github.com/chenasraf/simple-scaffold/compare/v0.4.4...v0.4.5) (2019-02-27)
## [0.4.5](https://github.com/chenasraf/simple-scaffold/compare/v0.4.4...v0.4.5) (2019-02-27)
### [0.4.4](https://github.com/chenasraf/simple-scaffold/compare/v0.4.3...v0.4.4) (2019-02-27)
## [0.4.4](https://github.com/chenasraf/simple-scaffold/compare/v0.4.3...v0.4.4) (2019-02-27)
### [0.4.3](https://github.com/chenasraf/simple-scaffold/compare/v0.4.2...v0.4.3) (2019-02-27)
## [0.4.3](https://github.com/chenasraf/simple-scaffold/compare/v0.4.2...v0.4.3) (2019-02-27)
### [0.4.2](https://github.com/chenasraf/simple-scaffold/compare/v0.4.1...v0.4.2) (2019-02-25)
## [0.4.2](https://github.com/chenasraf/simple-scaffold/compare/v0.4.1...v0.4.2) (2019-02-25)
### [0.4.1](https://github.com/chenasraf/simple-scaffold/compare/v0.3.1...v0.4.1) (2019-02-25)
## [0.4.1](https://github.com/chenasraf/simple-scaffold/compare/v0.3.1...v0.4.1) (2019-02-25)
### [0.3.1](https://github.com/chenasraf/simple-scaffold/compare/v0.3.0...v0.3.1) (2018-01-15)
## [0.3.1](https://github.com/chenasraf/simple-scaffold/compare/v0.3.0...v0.3.1) (2018-01-15)
## [0.3.0](https://github.com/chenasraf/simple-scaffold/compare/v0.2.0...v0.3.0) (2018-01-15)
## [0.2.0](https://github.com/chenasraf/simple-scaffold/compare/v0.1.5...v0.2.0) (2018-01-05)
### [0.1.5](https://github.com/chenasraf/simple-scaffold/compare/v0.1.4...v0.1.5) (2018-01-01)
## [0.1.5](https://github.com/chenasraf/simple-scaffold/compare/v0.1.4...v0.1.5) (2018-01-01)
### [0.1.4](https://github.com/chenasraf/simple-scaffold/compare/v0.1.3...v0.1.4) (2018-01-01)
## [0.1.4](https://github.com/chenasraf/simple-scaffold/compare/v0.1.3...v0.1.4) (2018-01-01)
### [0.1.3](https://github.com/chenasraf/simple-scaffold/compare/v0.1.2...v0.1.3) (2018-01-01)
## [0.1.3](https://github.com/chenasraf/simple-scaffold/compare/v0.1.2...v0.1.3) (2018-01-01)
### 0.1.2 (2018-01-01)
## 0.1.2 (2018-01-01)

413
README.md
View File

@@ -10,14 +10,18 @@
![build](https://img.shields.io/github/actions/workflow/status/chenasraf/simple-scaffold/release.yml?branch=master)
</h2>
Generate any set of files in the easiest way possible with simple commands.
It is completely agnostic and un-opinionated so you can use it for anything from a few simple files
to an entire app boilerplate setup.
Looking to streamline your workflow and get your projects up and running quickly? Look no further
than Simple Scaffold - the easy-to-use NPM package that simplifies the process of organizing and
copying your commonly-created files.
Simply organize your commonly-created files in their **original structure**, and running Simple
Scaffold will copy the files to the output path, while replacing values (such as component or app
name, or other custom data) inside the paths or contents of the files using Handlebars.js syntax.
With its agnostic and un-opinionated approach, Simple Scaffold can handle anything from a few simple
files to an entire app boilerplate setup. Plus, with the power of **Handlebars.js** syntax, you can
easily replace custom data and personalize your files to fit your exact needs. But that's not all -
you can also use it to loop through data, use conditions, and write custom functions using helpers.
Don't waste any more time manually copying and pasting files - let Simple Scaffold do the heavy
lifting for you and start building your projects faster and more efficiently today!
<div align="center">
@@ -25,385 +29,82 @@ name, or other custom data) inside the paths or contents of the files using Hand
</div>
<br />
<details>
<summary>Table of contents</summary>
- [Install](#install)
- [Command Line Interface (CLI)](#command-line-interface-cli)
- [Available flags](#available-flags)
- [Node module](#node-module)
- [Node-specific options](#node-specific-options)
- [Preparing files](#preparing-files)
- [Template files](#template-files)
- [Variable/token replacement](#variabletoken-replacement)
- [Helpers](#helpers)
- [Built-in Helpers](#built-in-helpers)
- [Capitalization Helpers](#capitalization-helpers)
- [Date helpers](#date-helpers)
- [Custom Helpers](#custom-helpers)
- [Examples](#examples)
- [Run](#run)
- [Command Example](#command-example)
- [Equivalent Node Module Example](#equivalent-node-module-example)
- [Files](#files)
- [Input](#input)
- [Output](#output)
- [Contributing](#contributing)
</details>
---
## Install
## Quick Start
You can either use it as a command line tool or import into your own code and run from there.
### Local Templates
```bash
# npm
npm install [-g] simple-scaffold
# yarn
yarn [global] add simple-scaffold
# run without installing
npx simple-scaffold@latest <...args>
```
The fastest way to get started is to use `npx` to immediately start a scaffold process.
## Command Line Interface (CLI)
Prepare any templates you want to use - for example, in the directory `templates/component`; and use
that in the CLI args. Here is a simple example file:
### Available flags
`templates/component/{{ pascalName name }}.tsx`
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`.
```tsx
// Created: {{ now | 'yyyy-MM-dd' }}
import React from 'react'
```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.
--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)
```
You can also add this as a script in your `package.json`:
```json
{
"scripts": {
"scaffold": "npx simple-scaffold@latest -t scaffolds/component/**/* -o src/components -d '{\"myProp\": \"propName\", \"myVal\": 123}'"
}
}
```
## Node module
You can also build the scaffold yourself, if you want to create more complex arguments or scaffold
groups - simply pass a config object to the Scaffold function when you are ready to start.
The config takes similar arguments to the command line:
```typescript
import Scaffold from "simple-scaffold"
const config = {
name: "component",
templates: [path.join(__dirname, "scaffolds", "component")],
output: path.join(__dirname, "src", "components"),
createSubFolder: true,
subFolderNameHelper: "upperCase"
data: {
property: "value",
},
helpers: {
twice: (text) => [text, text].join(" ")
},
beforeWrite: (content, rawContent, outputPath) => content.toString().toUpperCase()
}
const scaffold = Scaffold(config)
```
### Node-specific options
In addition to all the options available in the command line, there are some Node/JS-specific
options available:
| Option | Type | Description |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `output` | | In addition to being passed the same as CLI, it may also be passed a function for each input file to output into a dynamic path: `{ output: (fullPath, baseDir, baseName) => path.resolve(baseDir, baseName) }` |
| `helpers` | `Record<string, (string) => string>` | Helpers are simple functions that transform your `data` variables into other values. See [Helpers](#helpers) for the list of default helpers, or add your own to be loaded into the template parser. |
| `beforeWrite` | `(content: Buffer, rawContent: Buffer, outputPath: string) => Promise<String \| Buffer \| undefined> \| String \| Buffer \| undefined` | Supply this function to override the final output contents of each of your files, allowing you to add more pre-processing to your generator pipeline. The return value of this function will replace the output content of the respective file, which you may discriminate (if needed) using the `outputPath` argument. |
| `append-data` | | Does not exist in Node.JS options, use `data` instead. |
## Preparing files
### Template files
Put your template files anywhere, and fill them with tokens for replacement.
Each template (not file) in the config array is parsed individually, and copied to the output
directory. If a single template path contains multiple files (e.g. if you use a folder path or a
glob pattern), the first directory up the tree of that template will become the base inside the
defined output path for that template, while copying files recursively and maintaining their
relative structure.
Examples:
> In the following examples, the config `name` is `AppName`, and the config `output` is `src`.
| Input template | Files in template | Output path(s) |
| ----------------------------- | ------------------------------------------------------ | ------------------------------------------------------------ |
| `./templates/{{ name }}.txt` | `./templates/{{ name }}.txt` | `src/AppName.txt` |
| `./templates/directory` | `outer/{{name}}.txt`,<br />`outer2/inner/{{name}}.txt` | `src/outer/AppName.txt`,<br />`src/outer2/inner/AppName.txt` |
| `./templates/others/**/*.txt` | `outer/{{name}}.jpg`,<br />`outer2/inner/{{name}}.txt` | `src/outer2/inner/AppName.txt` |
### Variable/token replacement
Scaffolding will replace `{{ varName }}` in both the file name and its contents and put the
transformed files in the output directory.
The data available for the template parser is the data you pass to the `data` config option (or
`--data` argument in CLI).
For example, using the following command:
```bash
npx simple-scaffold@latest \
--templates templates/components/{{name}}.jsx \
--output src/components \
--create-sub-folder true \
MyComponent
```
Will output a file with the path:
```text
<working_dir>/src/components/MyComponent.jsx
```
The contents of the file will be transformed in a similar fashion.
Your `data` will be pre-populated with the following:
- `{{Name}}`: PascalCase of the component name
- `{{name}}`: raw name of the component as you entered it
> Simple-Scaffold uses [Handlebars.js](https://handlebarsjs.com/) for outputting the file contents.
> Any `data` you add in the config will be available for use with their names wrapped in `{{` and
> `}}`. Other Handlebars built-ins such as `each`, `if` and `with` are also supported, see
> [Handlebars.js Language Features](https://handlebarsjs.com/guide/#language-features) for more
> information.
### Helpers
#### Built-in Helpers
Simple-Scaffold provides some built-in text transformation filters usable by Handlebars.
For example, you may use `{{ snakeCase name }}` inside a template file or filename, and it will
replace `My Name` with `my_name` when producing the final value.
##### Capitalization Helpers
| Helper name | Example code | Example output |
| ------------ | ----------------------- | -------------- |
| [None] | `{{ name }}` | my name |
| `camelCase` | `{{ camelCase name }}` | myName |
| `snakeCase` | `{{ snakeCase name }}` | my_name |
| `startCase` | `{{ startCase name }}` | My Name |
| `kebabCase` | `{{ kebabCase name }}` | my-name |
| `hyphenCase` | `{{ hyphenCase name }}` | my-name |
| `pascalCase` | `{{ pascalCase name }}` | MyName |
| `upperCase` | `{{ upperCase name }}` | MY NAME |
| `lowerCase` | `{{ lowerCase name }}` | my name |
##### Date helpers
| Helper name | Description | Example code | Example output |
| -------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------- | ------------------ |
| `now` | Current date with format | `{{ now "yyyy-MM-dd HH:mm" }}` | `2042-01-01 15:00` |
| `now` (with offset) | Current date with format, and with offset | `{{ now "yyyy-MM-dd HH:mm" -1 "hours" }}` | `2042-01-01 14:00` |
| `date` | Custom date with format | `{{ date "2042-01-01T15:00:00Z" "yyyy-MM-dd HH:mm" }}` | `2042-01-01 15:00` |
| `date` (with offset) | Custom date with format, and with offset | `{{ date "2042-01-01T15:00:00Z" "yyyy-MM-dd HH:mm" -1 "days" }}` | `2041-31-12 15:00` |
| `date` (with date from `--data`) | Custom date with format, with data from the `data` config option | `{{ date myCustomDate "yyyy-MM-dd HH:mm" }}` | `2042-01-01 12:00` |
Further details:
- We use [`date-fns`](https://date-fns.org/docs/) for parsing/manipulating the dates. If you want
more information on the date tokens to use, refer to
[their format documentation](https://date-fns.org/docs/format).
- The date helper format takes the following arguments:
```typescript
(
date: string,
format: string,
offsetAmount?: number,
offsetType?: "years" | "months" | "weeks" | "days" | "hours" | "minutes" | "seconds"
export default {{pascalCase name}}: React.FC = (props) => {
return (
<div className="{{camelCase name}}">{{pascalCase name}} Component</div>
)
```
- **The now helper** (for current time) takes the same arguments, minus the first one (`date`) as it
is implicitly the current date.
#### Custom Helpers
You may also add your own custom helpers using the `helpers` options when using the JS API (rather
than the CLI). The `helpers` option takes an object whose keys are helper names, and values are the
transformation functions. For example, `upperCase` is implemented like so:
```typescript
config.helpers = {
upperCase: (text) => text.toUpperCase(),
}
```
All of the above helpers (built in and custom) will also be available to you when using
`subFolderNameHelper` (`--sub-folder-name-helper`/`-sh`) as a possible value.
To generate the template output, run:
> To see more information on how helpers work and more features, see
> [Handlebars.js docs](https://handlebarsjs.com/guide/#custom-helpers).
## Examples
### Run
#### Command Example
```bash
simple-scaffold MyComponent \
-t project/scaffold/**/* \
-o src/components \
-d '{"className": "myClassName","author": "Chen Asraf"}'
MyComponent
```shell
# generate single component
npx simple-scaffold@latest \
-t templates/component -o src/components PageWrapper
```
#### Equivalent Node Module Example
This will immediately create the following file: `src/components/PageWrapper.tsx`
```typescript
import Scaffold from "simple-scaffold"
```tsx
// Created: 2077/01/01
import React from 'react'
async function main() {
await Scaffold({
name: "MyComponent",
templates: ["project/scaffold/**/*"],
output: ["src/components"],
data: {
className: "myClassName",
author: "Chen Asraf",
},
})
console.log("Done.")
export default PageWrapper: React.FC = (props) => {
return (
<div className="pageWrapper">PageWrapper Component</div>
)
}
```
### Files
### Remote Templates
#### Input
Another quick way to start is to re-use someone else's (or your own) work using a template
repository.
- Input file path:
Here is an example for loading the example component templates in this very repository:
```text
project → scaffold → {{Name}}.js → src → components
```
```shell
npx simple-scaffold@latest \
-gh chenasraf/simple-scaffold#examples/test-input/scaffold.config.js:component \
PageWrapper
- Input file contents:
# equivalent to:
npx simple-scaffold@latest \
-c https://github.com/chenasraf/simple-scaffold.git#examples/test-input/scaffold.config.js:component \
PageWrapper
```
```typescript
/**
* Author: {{ author }}
* Date: {{ now "yyyy-MM-dd" }}
*/
import React from 'react'
When template name (`:component`) is omitted, `default` is used.
export default {{camelCase name}}: React.FC = (props) => {
return (
<div className="{{className}}">{{camelCase name}} Component</div>
)
}
```
See more at the [CLI documentation](https://chenasraf.github.io/simple-scaffold/pages/cli.html)
#### Output
## Documentation
- Output file path:
See full documentation [here](https://chenasraf.github.io/simple-scaffold).
- With `createSubFolder = false` (default):
```text
project → src → components → MyComponent.js
```
- With `createSubFolder = true`:
```text
project → src → components → MyComponent → MyComponent.js
```
- With `createSubFolder = true` and `subFolderNameHelper = 'upperCase'`:
```text
project → src → components → MYCOMPONENT → MyComponent.js
```
- Output file contents:
```typescript
/**
* Author: Chen Asraf
* Date: 2022-01-01
*/
import React from 'react'
export default MyComponent: React.FC = (props) => {
return (
<div className="myClassName">MyComponent Component</div>
)
}
```
- [Command Line Interface (CLI) usage](https://chenasraf.github.io/simple-scaffold/pages/cli.html)
- [Node.js usage](https://chenasraf.github.io/simple-scaffold/pages/node.html)
- [Templates](https://chenasraf.github.io/simple-scaffold/pages/templates.html)
- [Configuration Files](https://chenasraf.github.io/simple-scaffold/pages/configuration_files.html)
- [Migrating v0.x to v1.x](https://chenasraf.github.io/simple-scaffold/pages/migration.html)
## Contributing

View File

@@ -0,0 +1,13 @@
/** @type {import('simple-scaffold').ScaffoldConfigFile} */
module.exports = {
default: {
templates: ["examples/test-input/Component"],
output: "examples/test-output",
data: { property: "myProp", value: "10" },
},
component: {
templates: ["examples/test-input/Component"],
output: "examples/test-output/component",
data: { property: "myProp", value: "10" },
},
}

View File

@@ -1,6 +1,6 @@
{
"name": "simple-scaffold",
"version": "1.2.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",
@@ -44,6 +44,7 @@
"util.promisify": "^1.1.1"
},
"devDependencies": {
"@knodes/typedoc-plugin-pages": "^0.23.4",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/release-notes-generator": "^10.0.3",
@@ -62,8 +63,5 @@
"ts-node": "^10.9.1",
"typedoc": "^0.24.6",
"typescript": "^5.0.4"
},
"peerDependencies": {
"doc-theme": "file:./doc-theme"
}
}

7
pages/README.md Normal file
View File

@@ -0,0 +1,7 @@
See full documentation [here](https://chenasraf.github.io/simple-scaffold).
- [Command Line Interface (CLI) usage](https://chenasraf.github.io/simple-scaffold/pages/cli.html)
- [Node.js usage](https://chenasraf.github.io/simple-scaffold/pages/node.html)
- [Templates](https://chenasraf.github.io/simple-scaffold/pages/templates.html)
- [Configuration Files](https://chenasraf.github.io/simple-scaffold/pages/configuration_files.html)
- [Migrating v0.x to v1.x](https://chenasraf.github.io/simple-scaffold/pages/migration.html)

164
pages/cli.md Normal file
View File

@@ -0,0 +1,164 @@
## 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 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
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.
(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)
```
> See
> [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`:
```json
{
"scripts": {
"scaffold": "npx simple-scaffold@latest -t scaffolds/component/**/* -o src/components -d '{\"myProp\": \"propName\", \"myVal\": 123}'"
}
}
```

View File

@@ -0,0 +1,134 @@
If you want to have reusable configurations which are complex and don't fit into command lines
easily, or just want to manage your templates easier, you can use configuration files to load your
scaffolding configurations.
## Creating config files
Configuration files should be valid `.js`/`.json` files that contain valid Scaffold configurations.
Each file hold multiple scaffolds. Each scaffold is a key, and its value is the configuration. For
example:
```json
{
"component": {
"templates": ["templates/component"],
"output": "src/components"
}
}
```
The configuration contents are identical to the
[Node.js configuration structure](https://chenasraf.github.io/simple-scaffold/pages/node.md):
```ts
interface ScaffoldConfig {
name: string
templates: string[]
output: FileResponse<string>
createSubFolder?: boolean
data?: Record<string, any>
overwrite?: FileResponse<boolean>
quiet?: boolean
verbose?: LogLevel
dryRun?: boolean
helpers?: Record<string, Helper>
subFolderNameHelper?: DefaultHelpers | string
beforeWrite?(
content: Buffer,
rawContent: Buffer,
outputPath: string,
): string | Buffer | undefined | Promise<string | Buffer | undefined>
}
```
If you want to supply functions inside the configurations, you must use a `.js` file as JSON does
not support non-primitives.
A `.js` file is just like a `.json` file, make sure to export the final configuration:
```js
/** @type {import('simple-scaffold').ScaffoldConfigFile} */
module.exports = {
component: {
templates: ["templates/component"],
output: "src/components",
},
}
```
## 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), 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
```
If you don't want to supply a template/config name (e.g. `component`), you can omit the colon and
the name, and it will use the configuration named `default`:
```js
/** @type {import('simple-scaffold').ScaffoldConfigFile} */
module.exports = {
default: {
// ...
},
}
```
And then:
```shell
# 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
```

View File

@@ -1,5 +1,3 @@
# Migrating from 0.x to 1.x
In Simple Scaffold v1.0, the entire codebase was overhauled, yet usage remains mostly the same
between versions. With these notable exceptions:
@@ -13,8 +11,10 @@ between versions. With these notable exceptions:
- `locals` has been renamed to `data`. The appropriate command line args have been updated as well
to `--data` | `-d`.
- Additional options have been added to both CLI and Node interfaces. See the [readme](/README.md)
for more information.
- Additional options have been added to both CLI and Node interfaces. See
[Command Line Interface (CLI) usage](https://chenasraf.github.io/simple-scaffold/pages/cli.html)
and [Node.js usage](https://chenasraf.github.io/simple-scaffold/pages/node.html) for more
information.
## Template syntax changes

53
pages/node.md Normal file
View File

@@ -0,0 +1,53 @@
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.
The config takes similar arguments to the command line. The full type definitions can be found in
[src/types.ts](https://github.com/chenasraf/simple-scaffold/blob/develop/src/types.ts#L13).
See the full
[documentation](https://chenasraf.github.io/simple-scaffold/interfaces/ScaffoldConfig.html) for the
configuration options and their behavior.
```ts
interface ScaffoldConfig {
name: string
templates: string[]
output: FileResponse<string>
createSubFolder?: boolean
data?: Record<string, any>
overwrite?: FileResponse<boolean>
quiet?: boolean
verbose?: LogLevel
dryRun?: boolean
helpers?: Record<string, Helper>
subFolderNameHelper?: DefaultHelpers | string
beforeWrite?(
content: Buffer,
rawContent: Buffer,
outputPath: string,
): string | Buffer | undefined | Promise<string | Buffer | undefined>
}
```
This is an example of loading a complete scaffold via Node.js:
```typescript
import Scaffold from "simple-scaffold"
const config = {
name: "component",
templates: [path.join(__dirname, "scaffolds", "component")],
output: path.join(__dirname, "src", "components"),
createSubFolder: true,
subFolderNameHelper: "upperCase"
data: {
property: "value",
},
helpers: {
twice: (text) => [text, text].join(" ")
},
beforeWrite: (content, rawContent, outputPath) => content.toString().toUpperCase()
}
const scaffold = Scaffold(config)
```

224
pages/templates.md Normal file
View File

@@ -0,0 +1,224 @@
# Preparing template files
Put your template files anywhere, and fill them with tokens for replacement.
Each template (not file) in the config array is parsed individually, and copied to the output
directory. If a single template path contains multiple files (e.g. if you use a folder path or a
glob pattern), the first directory up the tree of that template will become the base inside the
defined output path for that template, while copying files recursively and maintaining their
relative structure.
Examples:
> In the following examples, the config `name` is `AppName`, and the config `output` is `src`.
| Input template | Files in template | Output path(s) |
| ----------------------------- | ------------------------------------------------------ | ------------------------------------------------------------ |
| `./templates/{{ name }}.txt` | `./templates/{{ name }}.txt` | `src/AppName.txt` |
| `./templates/directory` | `outer/{{name}}.txt`,<br />`outer2/inner/{{name}}.txt` | `src/outer/AppName.txt`,<br />`src/outer2/inner/AppName.txt` |
| `./templates/others/**/*.txt` | `outer/{{name}}.jpg`,<br />`outer2/inner/{{name}}.txt` | `src/outer2/inner/AppName.txt` |
## Variable/token replacement
Scaffolding will replace `{{ varName }}` in both the file name and its contents and put the
transformed files in the output directory.
The data available for the template parser is the data you pass to the `data` config option (or
`--data` argument in CLI).
For example, using the following command:
```bash
npx simple-scaffold@latest \
--templates templates/components/{{name}}.jsx \
--output src/components \
--create-sub-folder true \
MyComponent
```
Will output a file with the path:
```text
<working_dir>/src/components/MyComponent.jsx
```
The contents of the file will be transformed in a similar fashion.
Your `data` will be pre-populated with the following:
- `{{Name}}`: PascalCase of the component name
- `{{name}}`: raw name of the component as you entered it
> Simple-Scaffold uses [Handlebars.js](https://handlebarsjs.com/) for outputting the file contents.
> Any `data` you add in the config will be available for use with their names wrapped in `{{` and
> `}}`. Other Handlebars built-ins such as `each`, `if` and `with` are also supported, see
> [Handlebars.js Language Features](https://handlebarsjs.com/guide/#language-features) for more
> information.
## Helpers
### Built-in Helpers
Simple-Scaffold provides some built-in text transformation filters usable by Handlebars.
For example, you may use `{{ snakeCase name }}` inside a template file or filename, and it will
replace `My Name` with `my_name` when producing the final value.
#### Capitalization Helpers
| Helper name | Example code | Example output |
| ------------ | ----------------------- | -------------- |
| [None] | `{{ name }}` | my name |
| `camelCase` | `{{ camelCase name }}` | myName |
| `snakeCase` | `{{ snakeCase name }}` | my_name |
| `startCase` | `{{ startCase name }}` | My Name |
| `kebabCase` | `{{ kebabCase name }}` | my-name |
| `hyphenCase` | `{{ hyphenCase name }}` | my-name |
| `pascalCase` | `{{ pascalCase name }}` | MyName |
| `upperCase` | `{{ upperCase name }}` | MY NAME |
| `lowerCase` | `{{ lowerCase name }}` | my name |
#### Date helpers
| Helper name | Description | Example code | Example output |
| -------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------- | ------------------ |
| `now` | Current date with format | `{{ now "yyyy-MM-dd HH:mm" }}` | `2042-01-01 15:00` |
| `now` (with offset) | Current date with format, and with offset | `{{ now "yyyy-MM-dd HH:mm" -1 "hours" }}` | `2042-01-01 14:00` |
| `date` | Custom date with format | `{{ date "2042-01-01T15:00:00Z" "yyyy-MM-dd HH:mm" }}` | `2042-01-01 15:00` |
| `date` (with offset) | Custom date with format, and with offset | `{{ date "2042-01-01T15:00:00Z" "yyyy-MM-dd HH:mm" -1 "days" }}` | `2041-31-12 15:00` |
| `date` (with date from `--data`) | Custom date with format, with data from the `data` config option | `{{ date myCustomDate "yyyy-MM-dd HH:mm" }}` | `2042-01-01 12:00` |
Further details:
- We use [`date-fns`](https://date-fns.org/docs/) for parsing/manipulating the dates. If you want
more information on the date tokens to use, refer to
[their format documentation](https://date-fns.org/docs/format).
- The date helper format takes the following arguments:
```typescript
(
date: string,
format: string,
offsetAmount?: number,
offsetType?: "years" | "months" | "weeks" | "days" | "hours" | "minutes" | "seconds"
)
```
- **The now helper** (for current time) takes the same arguments, minus the first one (`date`) as it
is implicitly the current date.
### Custom Helpers
You may also add your own custom helpers using the `helpers` options when using the JS API (rather
than the CLI). The `helpers` option takes an object whose keys are helper names, and values are the
transformation functions. For example, `upperCase` is implemented like so:
```typescript
config.helpers = {
upperCase: (text) => text.toUpperCase(),
}
```
All of the above helpers (built in and custom) will also be available to you when using
`subFolderNameHelper` (`--sub-folder-name-helper`/`-sh`) as a possible value.
> To see more information on how helpers work and more features, see
> [Handlebars.js docs](https://handlebarsjs.com/guide/#custom-helpers).
# Examples
## Run
### Command Example
```bash
simple-scaffold MyComponent \
-t project/scaffold/**/* \
-o src/components \
-d '{"className": "myClassName","author": "Chen Asraf"}'
MyComponent
```
### Equivalent Node Module Example
```typescript
import Scaffold from "simple-scaffold"
async function main() {
await Scaffold({
name: "MyComponent",
templates: ["project/scaffold/**/*"],
output: ["src/components"],
data: {
className: "myClassName",
author: "Chen Asraf",
},
})
console.log("Done.")
}
```
## Files
### Input
- Input file path:
```text
project → scaffold → {{Name}}.js → src → components
```
- Input file contents:
```typescript
/**
* Author: {{ author }}
* Date: {{ now "yyyy-MM-dd" }}
*/
import React from 'react'
export default {{camelCase name}}: React.FC = (props) => {
return (
<div className="{{className}}">{{camelCase name}} Component</div>
)
}
```
### Output
- Output file path:
- With `createSubFolder = false` (default):
```text
project → src → components → MyComponent.js
```
- With `createSubFolder = true`:
```text
project → src → components → MyComponent → MyComponent.js
```
- With `createSubFolder = true` and `subFolderNameHelper = 'upperCase'`:
```text
project → src → components → MYCOMPONENT → MyComponent.js
```
- Output file contents:
```typescript
/**
* Author: Chen Asraf
* Date: 2077-01-01
*/
import React from 'react'
export default MyComponent: React.FC = (props) => {
return (
<div className="myClassName">MyComponent Component</div>
)
}
```

View File

@@ -1,3 +1,14 @@
const releaseRules = [
{ type: "feat", section: "Features", release: "minor" },
{ type: "docs", section: "Build", release: false },
{ type: "fix", section: "Bug Fixes", release: "patch" },
{ type: "refactor", section: "Misc", release: "patch" },
{ type: "perf", section: "Misc", release: "patch" },
{ type: "build", section: "Build", release: "patch" },
{ type: "chore", section: "Misc", release: "patch" },
{ type: "test", section: "Tests", release: "patch" },
]
/** @type {import('semantic-release').Options} */
module.exports = {
branches: [
@@ -5,6 +16,7 @@ module.exports = {
"master",
"next",
"next-major",
{ name: "develop", prerelease: true },
{ name: "beta", prerelease: true },
{ name: "alpha", prerelease: true },
],
@@ -22,17 +34,7 @@ module.exports = {
parserOpts: {
noteKeywords: ["breaking:", "breaking-fix:", "breaking-feat:"],
},
releaseRules: [
{ type: "feat", section: "Features", release: "minor" },
{ type: "docs", section: "Misc", release: "patch" },
{ type: "fix", section: "Bug Fixes", release: "patch" },
{ type: "refactor", section: "Misc", release: "patch" },
{ type: "docs", section: "Misc", release: "patch" },
{ type: "perf", section: "Misc", release: "patch" },
{ type: "build", section: "Misc", release: "patch" },
{ type: "chore", section: "Misc", release: "patch" },
{ type: "test", section: "Misc", release: "patch" },
],
releaseRules: releaseRules,
},
],
[
@@ -41,17 +43,7 @@ module.exports = {
preset: "conventionalcommits",
parserOpts: {
noteKeywords: ["breaking"],
types: [
{ type: "feat", section: "Features", release: "minor" },
{ type: "docs", section: "Misc", release: "patch" },
{ type: "fix", section: "Bug Fixes", release: "patch" },
{ type: "refactor", section: "Misc", release: "patch" },
{ type: "docs", section: "Misc", release: "patch" },
{ type: "perf", section: "Misc", release: "patch" },
{ type: "build", section: "Misc", release: "patch" },
{ type: "chore", section: "Misc", release: "patch" },
{ type: "test", section: "Misc", release: "patch" },
],
types: releaseRules,
},
},
],
@@ -65,13 +57,7 @@ module.exports = {
[
"@semantic-release/npm",
{
packageRoot: "dist",
},
],
[
"@semantic-release/github",
{
assets: ["package.tgz"],
pkgRoot: "dist",
},
],
[
@@ -80,5 +66,11 @@ module.exports = {
assets: ["CHANGELOG.md", "package.json"],
},
],
[
"@semantic-release/github",
{
assets: ["package.tgz"],
},
],
],
}

View File

@@ -5,17 +5,17 @@ import { LogLevel, ScaffoldCmdConfig } from "./types"
import { Scaffold } from "./scaffold"
import path from "path"
import fs from "fs/promises"
import { parseAppendData } from "./utils"
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") || args.includes("--github") || args.includes("-gh")
return (
massarg<ScaffoldCmdConfig>()
.main((config) => {
config.data = { ...config.data, ...config.appendData }
delete config.appendData
return Scaffold(config)
.main(async (config) => {
const _config = await parseConfig(config)
return Scaffold(_config)
})
.option({
name: "name",
@@ -25,11 +25,29 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
isDefault: true,
required: true,
})
.option({
name: "config",
aliases: ["c"],
description:
"Filename or https git URL to load config from instead of passing arguments to CLI or using a Node.js script. 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",
aliases: ["o"],
description: `Path to output to. If --create-sub-folder is enabled, the subfolder will be created inside this path. ${chalk.reset`${chalk.white`(default: current dir)`}`}`,
required: true,
required: !isConfig,
})
.option({
name: "templates",
@@ -38,7 +56,7 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
description:
"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.",
required: true,
required: !isConfig,
})
.option({
name: "overwrite",

View File

@@ -1,7 +1,6 @@
.tsd-typography table {
border-collapse: collapse;
width: 100%;
box-sizing
}
.tsd-typography table td,
@@ -10,32 +9,29 @@
border: 1px solid var(--color-accent);
padding: 6px;
}
.tsd-typography h1 + table,
.tsd-typography h2 + table,
.tsd-typography h3 + table,
.tsd-typography h4 + table,
.tsd-typography h5 + table,
.tsd-typography h6 + table,
.tsd-typography h1 + pre,
.tsd-typography h2 + pre,
.tsd-typography h3 + pre,
.tsd-typography h4 + pre,
.tsd-typography h5 + pre,
.tsd-typography h6 + pre {
.tsd-typography h6 + pre,
/* */
.tsd-typography h1 + table,
.tsd-typography h2 + table,
.tsd-typography h3 + table,
.tsd-typography h4 + table,
.tsd-typography h5 + table,
.tsd-typography h6 + table {
margin-top: 1em;
}
.tsd-typography pre + a + h1,
.tsd-typography pre + a + h2,
.tsd-typography pre + a + h3,
.tsd-typography pre + a + h4,
.tsd-typography pre + a + h5,
.tsd-typography pre + a + h6,
/* */
.tsd-typography table + a + h1,
.tsd-typography table + a + h2,
.tsd-typography table + a + h3,
@@ -44,3 +40,16 @@
.tsd-typography table + a + h6 {
margin-top: 2em;
}
.tsd-index-accordion[data-key*="Configuration."] ul.tsd-nested-navigation,
.tsd-index-accordion[data-key*="Configuration."] .tsd-accordion-summary > svg,
.tsd-index-accordion[data-key="Changelog"] ul.tsd-nested-navigation,
.tsd-index-accordion[data-key="Changelog"] .tsd-accordion-summary > svg,
.tsd-index-accordion[data-key="Configuration"] li:nth-child(n + 6) {
display: none;
}
.tsd-index-accordion[data-key*="Configuration."],
.tsd-index-accordion[data-key="Changelog"] {
margin-left: 0;
}

View File

@@ -336,4 +336,9 @@ export interface ScaffoldCmdConfig {
quiet: boolean
verbose: LogLevel
dryRun: boolean
config?: string
key?: string
github?: string
}
export type ScaffoldConfigFile = Record<string, ScaffoldConfig>

View File

@@ -8,6 +8,7 @@ import {
LogLevel,
ScaffoldCmdConfig,
ScaffoldConfig,
ScaffoldConfigFile,
} from "./types"
import camelCase from "lodash/camelCase"
import snakeCase from "lodash/snakeCase"
@@ -22,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,
@@ -98,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",
@@ -109,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]
@@ -369,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)
}
@@ -395,3 +405,104 @@ export function parseAppendData(value: string, options: ScaffoldCmdConfig & Opti
function isWrappedWithQuotes(string: string): boolean {
return (string.startsWith('"') && string.endsWith('"')) || (string.startsWith("'") && string.endsWith("'"))
}
/** @internal */
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 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[key],
data: {
...configImport[key].data,
...config.data,
},
}
}
c.data = { ...c.data, ...config.appendData }
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
}

63
typedoc.config.js Normal file
View File

@@ -0,0 +1,63 @@
const path = require("path")
/** @type {import('typedoc').TypeDocOptions} */
module.exports = {
name: "Simple Scaffold",
entryPoints: ["src/index.ts"],
includeVersion: true,
categorizeByGroup: false,
sort: ["visibility"],
categoryOrder: ["Main", "*"],
media: "media",
githubPages: true,
entryPointStrategy: "expand",
out: "docs",
excludePrivate: true,
excludeProtected: true,
excludeInternal: true,
gaID: "GTM-KHQS9TQ",
validation: {
invalidLink: true,
},
plugin: ["@knodes/typedoc-plugin-pages"],
customCss: "src/docs.css",
options: "typedoc.config.js",
logLevel: "Verbose",
pluginPages: {
logLevel: "Verbose",
pages: [
{
name: "Configuration",
source: "README.md",
childrenDir: path.join(process.cwd(), "pages"),
childrenOutputDir: "./",
children: [
{
name: "CLI usage",
source: "cli.md",
},
{
name: "Node.js usage",
source: "node.md",
},
{
name: "Templates",
source: "templates.md",
},
{
name: "Configuration Files",
source: "configuration_files.md",
},
{
name: "Migrating v0.x to v1.x",
source: "migration.md",
},
],
},
{
name: "Changelog",
source: "./CHANGELOG.md",
},
],
},
}

View File

@@ -1,27 +0,0 @@
{
"name": "Simple Scaffold",
"entryPoints": [
"src/index.ts"
],
"includeVersion": true,
"categorizeByGroup": false,
"sort": [
"visibility"
],
"categoryOrder": [
"Main",
"*"
],
"media": "media",
"githubPages": true,
"entryPointStrategy": "expand",
"out": "docs",
"excludePrivate": true,
"excludeProtected": true,
"excludeInternal": true,
"gaID": "GTM-KHQS9TQ",
"validation": {
"invalidLink": true
},
"customCss": "src/docs.css",
}

View File

@@ -691,6 +691,24 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
"@knodes/typedoc-plugin-pages@^0.23.4":
version "0.23.4"
resolved "https://registry.yarnpkg.com/@knodes/typedoc-plugin-pages/-/typedoc-plugin-pages-0.23.4.tgz#0a647e6c2c48a4d1cd1092d1bfa2e5a2668cd118"
integrity sha512-3LMS2TDV/lH/BKfxNEjCEhAwSCxOuQRF742EoYoP63/AQmIM/EZKDwShn1AYu4CtVAHvJpixuKF0VeFDLjgrbQ==
dependencies:
"@knodes/typedoc-pluginutils" "~0.23.4"
lodash "^4.17.21"
"@knodes/typedoc-pluginutils@~0.23.4":
version "0.23.4"
resolved "https://registry.yarnpkg.com/@knodes/typedoc-pluginutils/-/typedoc-pluginutils-0.23.4.tgz#9132c2a9247d46dfa9bb2816f4a3f627005f933a"
integrity sha512-uO7t9XxYW1DMjVFTgmRs2p431+AsmvnX4CrrB/zkauo8mZSLS3CppMKHBVE/Lk1cFO73vgfyJReRK98X/8BzUA==
dependencies:
fastest-levenshtein "^1.0.16"
lodash "^4.17.21"
pkg-up "^3.1.0"
semver "^7.3.5"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -2491,6 +2509,13 @@ find-up@^2.0.0:
dependencies:
locate-path "^2.0.0"
find-up@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
dependencies:
locate-path "^3.0.0"
find-up@^4.0.0, find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
@@ -3843,6 +3868,14 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
dependencies:
p-locate "^3.0.0"
path-exists "^3.0.0"
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
@@ -4636,7 +4669,7 @@ p-limit@^1.1.0:
dependencies:
p-try "^1.0.0"
p-limit@^2.2.0:
p-limit@^2.0.0, p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
@@ -4664,6 +4697,13 @@ p-locate@^2.0.0:
dependencies:
p-limit "^1.1.0"
p-locate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
dependencies:
p-limit "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
@@ -4895,6 +4935,13 @@ pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
pkg-up@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
dependencies:
find-up "^3.0.0"
postcss-selector-parser@^6.0.10:
version "6.0.11"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc"