Compare commits

...

115 Commits

Author SHA1 Message Date
semantic-release-bot
179fabc579 chore(release): 1.10.0 [skip ci]
# [1.10.0](https://github.com/chenasraf/simple-scaffold/compare/v1.9.0...v1.10.0) (2024-01-31)

### Bug Fixes

* remove gh flag ([939200c](939200c9f2))
* tests ([ff92fd7](ff92fd7607))

### Features

* try multiple default config files ([89aacb5](89aacb58fd))
2024-01-31 22:22:21 +00:00
909fe5bbea fix!: version number 2024-02-01 00:21:49 +02:00
semantic-release-bot
bc7d687977 chore(release): 1.10.0 [skip ci]
# [1.10.0](https://github.com/chenasraf/simple-scaffold/compare/v1.9.0...v1.10.0) (2024-01-31)

### Bug Fixes

* remove gh flag ([939200c](939200c9f2))
* tests ([ff92fd7](ff92fd7607))

### Features

* try multiple default config files ([89aacb5](89aacb58fd))
2024-01-31 22:15:13 +00:00
e6d9816a2f ci: fix release 2024-02-01 00:14:32 +02:00
e2353134d4 chore: update deps 2024-01-31 23:55:39 +02:00
6c7e3e5068 build: fix versioning 2024-01-31 23:55:39 +02:00
81dd6e3b19 docs: fix usage index link 2024-01-31 23:55:39 +02:00
b852a956ba docs: update badges 2024-01-31 23:55:39 +02:00
0ecc2590c7 docs: update badges 2024-01-31 23:55:39 +02:00
af4b753a6d docs: fix intro gif 2024-01-31 23:55:39 +02:00
semantic-release-bot
a8162f2315 chore(release): 2.0.0-pre.3 [skip ci]
# [2.0.0-pre.3](https://github.com/chenasraf/simple-scaffold/compare/v2.0.0-pre.2...v2.0.0-pre.3) (2024-01-30)

### Bug Fixes

* tests ([c219d38](c219d382e6))
2024-01-31 23:55:39 +02:00
ff92fd7607 fix: tests 2024-01-31 23:55:39 +02:00
0dc1116141 feat!: rename createSubFolder and subFolderNameHelper 2024-01-31 23:55:39 +02:00
f36cf4c2f2 docs: regenerate changelog
d
2024-01-31 23:55:39 +02:00
Chen Asraf
dd58c7fdda Update README.md 2024-01-31 23:55:39 +02:00
semantic-release-bot
058f1d4afa chore(release): 2.0.0-pre.2 [skip ci]
# [2.0.0-pre.2](https://github.com/chenasraf/simple-scaffold/compare/v2.0.0-pre.1...v2.0.0-pre.2) (2024-01-29)
2024-01-31 23:55:39 +02:00
583be2d9d2 docs: fix readme doc links
a
2024-01-31 23:55:39 +02:00
semantic-release-bot
18f1fac119 chore(release): 2.0.0-pre.1 [skip ci]
# [2.0.0-pre.1](https://github.com/chenasraf/simple-scaffold/compare/v1.9.0...v2.0.0-pre.1) (2024-01-29)

### Bug Fixes

* remove gh flag ([e66d6ba](e66d6ba86a))

### Features

* try multiple default config files ([f25cda7](f25cda738b))
2024-01-31 23:55:39 +02:00
693b8c8ea4 ci: fix docs 2024-01-31 23:55:39 +02:00
901d5d76b4 ci: fix docs 2024-01-31 23:55:39 +02:00
fac571f588 ci: fix docs 2024-01-31 23:55:39 +02:00
a10f412337 ci: use tag versions 2024-01-31 23:55:39 +02:00
819cd20644 docs: gtag + update deps 2024-01-31 23:55:39 +02:00
dc4c940559 ci: fix docs command 2024-01-31 23:55:39 +02:00
816e2f9aa0 ci: fix 2024-01-31 23:55:39 +02:00
825bd096c2 chore: fix docs & formatting 2024-01-31 23:55:39 +02:00
939200c9f2 fix: remove gh flag 2024-01-31 23:55:39 +02:00
0b7653de72 ci: fix docs build dir 2024-01-31 23:55:39 +02:00
89aacb58fd feat: try multiple default config files 2024-01-31 23:55:39 +02:00
9ce2845ace feat!: separate git/github/config flags
feat: separate git/github/config

t

test

cleanup
2024-01-31 23:55:39 +02:00
5373495f80 docs: update 2024-01-31 23:55:39 +02:00
70a080cc8e docs: update docs 2024-01-31 23:55:39 +02:00
ef7811d7e2 docs: update readme image 2024-01-31 23:55:39 +02:00
8b6b958480 docs: update docs, remove generated files from git 2024-01-31 23:55:39 +02:00
2c73207784 docs: update 2024-01-31 23:55:39 +02:00
5252642f6b docs: docusaurus initial commit 2024-01-31 23:55:39 +02:00
361778a188 feat!: rename verbose to logLevel 2024-01-31 23:55:39 +02:00
a54b1d6297 chore: update deps 2024-01-31 23:55:39 +02:00
ae69eb52db chore: update deps 2024-01-31 23:55:39 +02:00
3b60f1816d docs: update docs 2024-01-31 23:55:39 +02:00
c04d1cc42a chore!: remove Name from default data 2024-01-31 23:55:39 +02:00
c6b185cd8f feat!: remove url colon syntax 2024-01-31 23:55:39 +02:00
5abf528b81 chore!: update massarg 2024-01-31 23:55:39 +02:00
aeb3680ddf ci: update docs build 2024-01-31 23:55:39 +02:00
40e2381d05 ci: update build 2024-01-31 23:55:39 +02:00
semantic-release-bot
4d1a6e1f0d chore(release): 1.9.0 [skip ci]
# [1.9.0](https://github.com/chenasraf/simple-scaffold/compare/v1.8.0...v1.9.0) (2024-01-02)

### Features

* add --recurse-submodules to git clone ([cbaf130](cbaf130a0c))
2024-01-02 00:20:20 +00:00
daae9c10f9 ci: fix actions 2024-01-02 02:19:46 +02:00
758719de5d chore: update dependencies 2024-01-02 02:19:46 +02:00
1903055d46 ci: update build steps 2024-01-02 02:19:46 +02:00
Claudio Barca
cbaf130a0c feat: add --recurse-submodules to git clone 2024-01-02 02:19:46 +02:00
e26fe2a3d4 ci: update build process 2024-01-02 02:19:46 +02:00
7e1acf0607 ci: update docs build, semantic release
docs: update config files entry
2024-01-02 02:19:46 +02:00
semantic-release-bot
f666c357f4 chore(release): 1.8.0 [skip ci]
## [1.8.0](https://github.com/chenasraf/simple-scaffold/compare/v1.7.2...v1.8.0) (2023-11-29)

### Bug Fixes

* **config:** fn config load ([457c904](457c90470b)), closes [#63](https://github.com/chenasraf/simple-scaffold/issues/63)
2023-11-29 22:16:52 +00:00
f5d55f234a docs: update configuration files docs 2023-11-30 00:12:11 +02:00
semantic-release-bot
746f924a22 chore(release): 1.8.0-pre.1 [skip ci]
## [1.8.0-pre.1](https://github.com/chenasraf/simple-scaffold/compare/v1.7.2...v1.8.0-pre.1) (2023-11-27)

### Bug Fixes

* **config:** fn config load ([457c904](457c90470b)), closes [#63](https://github.com/chenasraf/simple-scaffold/issues/63)
2023-11-30 00:12:11 +02:00
807c3e27e2 ci: update release config 2023-11-28 00:49:28 +02:00
b048841dac chore: update dependencies 2023-11-28 00:49:28 +02:00
457c90470b fix(config): fn config load
closes #63
2023-11-28 00:49:28 +02:00
dependabot[bot]
0fa1ad4db7 build(deps-dev): bump @babel/traverse from 7.21.5 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.21.5 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-28 00:49:28 +02:00
semantic-release-bot
d62eeeb8e4 chore(release): 1.7.2 [skip ci]
## [1.7.2](https://github.com/chenasraf/simple-scaffold/compare/v1.7.1...v1.7.2) (2023-08-20)

### Bug Fixes

* windows path resolution ([98ee000](98ee00031f))
2023-08-20 10:09:48 +00:00
Chen Asraf
565d1f33aa Merge pull request #61 from chenasraf/pre
v1.7.2
2023-08-20 13:08:25 +03:00
semantic-release-bot
9f58fff2cf chore(release): 1.7.2-pre.1 [skip ci]
## [1.7.2-pre.1](https://github.com/chenasraf/simple-scaffold/compare/v1.7.1...v1.7.2-pre.1) (2023-08-15)

### Bug Fixes

* windows path resolution ([98ee000](98ee00031f))
2023-08-15 19:34:02 +00:00
dbba81d053 ci: trigger on pre branch 2023-08-15 22:32:25 +03:00
9f5716e1b9 chore: bump version number 2023-08-15 22:31:38 +03:00
Chen Asraf
4cbb79bb3b Merge pull request #60 from chenasraf/fix/windows
[BUGFIX] Windows path resolution
2023-08-15 22:28:04 +03:00
98ee00031f fix: windows path resolution 2023-08-14 00:19:50 +03:00
8c3369a00d chore: formatting 2023-08-14 00:19:36 +03:00
20ef0cea9b chore: update logs 2023-08-14 00:08:16 +03:00
Chen Asraf
3413151358 test: add tests
chore: use node:* modules for builtins
2023-06-12 23:26:25 +03:00
Chen Asraf
d2a2fda1b1 chore: update dependencies 2023-06-12 23:18:50 +03:00
Chen Asraf
486c07a55b Merge branch 'master' into develop 2023-06-08 08:13:52 +03:00
semantic-release-bot
de05bca546 chore(release): 1.7.1 [skip ci]
## [1.7.1](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0...v1.7.1) (2023-06-07)

### Bug Fixes

* local config file load error ([2b74239](2b7423993b))
2023-06-07 23:56:29 +00:00
Chen Asraf
72d4cf58c5 Merge pull request #58 from chenasraf/develop
v1.7.1
2023-06-08 02:55:06 +03:00
Chen Asraf
2cf31e827e build: fix tsconfig 2023-06-08 02:52:45 +03:00
Chen Asraf
3714e8b3bd build: update release rules, add tests 2023-06-08 02:50:50 +03:00
semantic-release-bot
77be7c09d5 chore(release): 1.7.1-develop.1 [skip ci]
## [1.7.1-develop.1](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0...v1.7.1-develop.1) (2023-06-07)

### Bug Fixes

* local config file load error ([2b74239](2b7423993b))
2023-06-07 21:51:53 +00:00
Chen Asraf
1246b51cda Merge branch 'master' into develop
# Conflicts:
#	CHANGELOG.md
#	package.json
2023-06-08 00:50:24 +03:00
semantic-release-bot
fea6c0fb16 chore(release): 1.7.0-develop.7 [skip ci]
## [1.7.0-develop.7](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.6...v1.7.0-develop.7) (2023-06-07)

### Bug Fixes

* local config file load error ([2b74239](2b7423993b))
2023-06-07 20:22:57 +00:00
Chen Asraf
2b7423993b fix: local config file load error 2023-06-07 23:21:12 +03:00
semantic-release-bot
a47ba1186c chore(release): 1.7.0-develop.6 [skip ci]
## [1.7.0-develop.6](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.5...v1.7.0-develop.6) (2023-05-27)
2023-05-27 23:16:21 +00:00
Chen Asraf
06b1552dca docs: update README.md 2023-05-28 02:14:36 +03:00
semantic-release-bot
4868925511 chore(release): 1.7.0 [skip ci]
## [1.7.0](https://github.com/chenasraf/simple-scaffold/compare/v1.6.0...v1.7.0) (2023-05-17)

### Features

* function config file ([02a8ba1](02a8ba16cd))

### Bug Fixes

* use path.normalize ([565090a](565090a951))
2023-05-17 17:30:07 +00:00
Chen Asraf
79cfdbed38 Merge pull request #57 from chenasraf/develop
release
2023-05-17 20:28:02 +03:00
Chen Asraf
4f27b7b934 test: add tests 2023-05-13 02:36:26 +03:00
semantic-release-bot
bee430a40d chore(release): 1.7.0-develop.5 [skip ci]
## [1.7.0-develop.5](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.4...v1.7.0-develop.5) (2023-05-12)
2023-05-12 22:59:55 +00:00
Chen Asraf
3dfc920455 test: fix + add tests 2023-05-13 01:58:21 +03:00
Chen Asraf
68307d1378 refactor: remove lodash dependency 2023-05-13 01:29:59 +03:00
Chen Asraf
33e1d569a3 build: update workflows 2023-05-13 01:29:40 +03:00
semantic-release-bot
c446439b1a chore(release): 1.7.0-develop.4 [skip ci]
## [1.7.0-develop.4](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.3...v1.7.0-develop.4) (2023-05-11)
2023-05-11 21:03:55 +00:00
Chen Asraf
773fd00674 build: update package asset name 2023-05-12 00:02:36 +03:00
semantic-release-bot
22763f655e chore(release): 1.7.0-develop.3 [skip ci]
## [1.7.0-develop.3](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.2...v1.7.0-develop.3) (2023-05-11)
2023-05-11 20:28:22 +00:00
Chen Asraf
a54b1f9fa7 build: update package link 2023-05-11 23:26:59 +03:00
Chen Asraf
dac5527173 build: add packageManager key to package.json 2023-05-11 22:59:36 +03:00
Chen Asraf
339459cad8 build: update workflows 2023-05-11 22:55:52 +03:00
Chen Asraf
62b4a1cd2f build: update workflows 2023-05-11 22:52:36 +03:00
Chen Asraf
5844c080ec build: use pnpm 2023-05-11 22:35:59 +03:00
Chen Asraf
9393c546aa chore: cleanup 2023-05-11 22:35:48 +03:00
semantic-release-bot
263bf0bf36 chore(release): 1.7.0-develop.2 [skip ci]
## [1.7.0-develop.2](https://github.com/chenasraf/simple-scaffold/compare/v1.7.0-develop.1...v1.7.0-develop.2) (2023-05-10)
2023-05-10 21:03:34 +00:00
Chen Asraf
e0ed371adb build: update release.config.js 2023-05-10 22:18:07 +03:00
semantic-release-bot
87934fb3d1 chore(release): 1.7.0-develop.1 [skip ci]
## [1.7.0-develop.1](https://github.com/chenasraf/simple-scaffold/compare/v1.6.0...v1.7.0-develop.1) (2023-05-09)

### Features

* function config file ([02a8ba1](02a8ba16cd))

### Bug Fixes

* use path.normalize ([565090a](565090a951))
2023-05-09 21:15:15 +00:00
Chen Asraf
19b7ed5f06 build: fix build 2023-05-10 00:13:10 +03:00
Chen Asraf
c027e37d9a chore: cleanups 2023-05-10 00:10:21 +03:00
Chen Asraf
2251a9c727 chore: cleanups 2023-05-10 00:08:45 +03:00
Chen Asraf
02a8ba16cd feat: function config file 2023-05-09 22:51:40 +03:00
Chen Asraf
565090a951 fix: use path.normalize 2023-05-08 00:28:47 +03:00
Chen Asraf
943aad1564 docs: update docs 2023-05-06 02:23:03 +03:00
Chen Asraf
9fb4762c7b test: move scaffold.config.js 2023-05-06 02:22:42 +03:00
Chen Asraf
be92047d65 docs: update readme + author 2023-05-06 01:38:23 +03:00
semantic-release-bot
0940e843ac chore(release): 1.6.0 [skip ci]
## [1.6.0](https://github.com/chenasraf/simple-scaffold/compare/v1.5.0...v1.6.0) (2023-05-05)

### Features

* node.js function for remote configs ([ce5adbe](ce5adbe0f8))

### Bug Fixes

* move dependency to dev dependency ([d916d88](d916d88384))
2023-05-05 22:33:58 +00:00
Chen Asraf
a880bc9445 Merge pull request #56 from chenasraf/develop
Release 1.6.0
2023-05-06 01:32:25 +03:00
Chen Asraf
6c8eb02cbb build: fix package.json version update 2023-05-04 23:21:16 +03:00
Chen Asraf
08b048845f build: use pnpm instead of yarn 2023-05-04 23:00:24 +03:00
Chen Asraf
77e477e07f chore(release): bump version number 2023-05-04 22:16:48 +03:00
Chen Asraf
5ba6034b2f docs: fix changelog title 2023-05-04 22:12:35 +03:00
Chen Asraf
95dafdf839 docs: update docs 2023-05-04 22:08:39 +03:00
69 changed files with 17128 additions and 7617 deletions

View File

@@ -2,20 +2,32 @@ name: Documentation
on:
push:
branches: [ master, develop ]
branches: [master, pre, develop]
jobs:
docs:
name: Build Documentation
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 docs]')"
# if: "contains(github.event.head_commit.message, 'chore(release)')"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- name: Checkout
uses: actions/checkout@v3
with:
node-version: "18.x"
- run: yarn install --frozen-lockfile
- run: yarn build-docs
- uses: peaceiris/actions-gh-pages@v3
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "20.x"
- name: Install PNPM
run: npm i -g pnpm
- name: Install dependencies
run: |
pnpm install --frozen-lockfile
cd docs && pnpm install --frozen-lockfile
- name: Build Docs
run: pnpm docs:build
- name: Deploy on GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
publish_dir: ./docs/build

View File

@@ -2,16 +2,25 @@ name: Pull Requests
on:
pull_request:
branches: [master, develop]
branches: [master, pre, develop]
jobs:
build:
name: Test & Build PR
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- name: Checkout
uses: actions/checkout@v3
with:
node-version: "18.x"
- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn test
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "20.x"
- name: Install PNPM
run: npm i -g pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Tests
run: pnpm test
- name: Build Package
run: pnpm build

View File

@@ -1,34 +1,40 @@
name: Test & Build
name: Release
on:
push:
branches: [ master, develop, feat/*, fix/* ]
branches: [master, pre, develop]
permissions:
contents: read # for checkout
jobs:
test:
release:
name: Release
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
permissions:
contents: write # to be able to publish a GitHub release
issues: write # to be able to comment on released issues
pull-requests: write # to be able to comment on released pull requests
id-token: write # to enable use of OIDC for npm provenance
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- name: Checkout
uses: actions/checkout@v3
with:
node-version: "18.x"
- run: yarn install --frozen-lockfile
- run: yarn test
if: "!contains(github.event.head_commit.message, '[skip test]')"
build:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
needs: test
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "18.x"
- run: yarn install --frozen-lockfile
- run: yarn build
- run: cd ./dist && yarn pack --filename=../package.tgz
- run: yarn semantic-release
if: "!contains(github.event.head_commit.message, '[skip publish]')"
node-version: "20.x"
- name: Install PNPM
run: npm i -g pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Tests
run: pnpm test
- name: Build Package
run: pnpm build
- name: Pack
run: cd ./dist && pnpm pack --pack-destination=../
- name: Semantic Release
run: npx semantic-release
env:
NPM_TOKEN: "${{ secrets.NPM_TOKEN }}"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

2
.gitignore vendored
View File

@@ -64,5 +64,5 @@ examples/test-output/**/*
dist/
.DS_Store
tmp/
docs/
.nvmrc

View File

@@ -14,9 +14,6 @@
"variabletoken"
],
"[markdown]": {
"editor.rulers": [
87,
100
],
},
"editor.rulers": [87, 100]
}
}

6
.vscode/tasks.json vendored
View File

@@ -14,7 +14,7 @@
"problemMatcher": []
},
{
"command": "yarn typedoc --watch",
"command": "pnpm typedoc --watch",
"label": "typedoc --watch",
"type": "shell",
"problemMatcher": []
@@ -32,8 +32,8 @@
"problemMatcher": []
},
{
"command": "yarn test --watchAll",
"label": "yarn test --watchAll",
"command": "pnpm test --watchAll",
"label": "pnpm test --watchAll",
"type": "shell",
"problemMatcher": []
},

View File

@@ -1,177 +1,629 @@
# Change Log
## [1.6.0-develop.1](https://github.com/chenasraf/simple-scaffold/compare/v1.5.0...v1.6.0-develop.1) (2023-05-04)
# [1.10.0](https://github.com/chenasraf/simple-scaffold/compare/v1.9.0...v1.10.0) (2024-01-31)
### Bug Fixes
* remove gh flag ([939200c](https://github.com/chenasraf/simple-scaffold/commit/939200c9f21be240485ea602a73b983ba2f47aaf))
* tests ([ff92fd7](https://github.com/chenasraf/simple-scaffold/commit/ff92fd7607f1b86f36fc6b62652fdfc81cb391a3))
### Features
* node.js function for remote configs ([ce5adbe](https://github.com/chenasraf/simple-scaffold/commit/ce5adbe0f898a86db6046d7f66d83dfcaa519ad2))
* try multiple default config files ([89aacb5](https://github.com/chenasraf/simple-scaffold/commit/89aacb58fd90a892f4994c758c61c43b2a6b1fba))
# [1.10.0](https://github.com/chenasraf/simple-scaffold/compare/v1.9.0...v1.10.0) (2024-01-31)
### Bug Fixes
* move dependency to dev dependency ([d916d88](https://github.com/chenasraf/simple-scaffold/commit/d916d88384054e6c6b40e6299073f1d1acb4d29d))
## [1.5.0-develop.3](https://github.com/chenasraf/simple-scaffold/compare/v1.5.0-develop.2...v1.5.0-develop.3) (2023-05-04)
* remove gh flag ([939200c](https://github.com/chenasraf/simple-scaffold/commit/939200c9f21be240485ea602a73b983ba2f47aaf))
* tests ([ff92fd7](https://github.com/chenasraf/simple-scaffold/commit/ff92fd7607f1b86f36fc6b62652fdfc81cb391a3))
### Features
* node.js function for remote configs ([7e9022f](https://github.com/chenasraf/simple-scaffold/commit/7e9022f4331c8a1351642042c0215f9160b2768a))
* try multiple default config files ([89aacb5](https://github.com/chenasraf/simple-scaffold/commit/89aacb58fd90a892f4994c758c61c43b2a6b1fba))
## [1.5.0-develop.2](https://github.com/chenasraf/simple-scaffold/compare/v1.5.0-develop.1...v1.5.0-develop.2) (2023-05-03)
# [2.0.0-pre.3](https://github.com/chenasraf/simple-scaffold/compare/v2.0.0-pre.2...v2.0.0-pre.3) (2024-01-30)
### Bug Fixes
* move dependency to dev dependency ([408a940](https://github.com/chenasraf/simple-scaffold/commit/408a94085366bb4e39391fcfcfa7df78b06a480f))
* tests ([c219d38](https://github.com/chenasraf/simple-scaffold/commit/c219d382e6df8b2ca1157544a7785f6645d04be2))
## [1.5.0](https://github.com/chenasraf/simple-scaffold/compare/v1.4.0...v1.5.0) (2023-05-02)
## 2.0.0-pre.2 (2024-01-29)
* Update README.md ([e012d51](https://github.com/chenasraf/simple-scaffold/commit/e012d51))
* docs: fix readme doc links ([55e561b](https://github.com/chenasraf/simple-scaffold/commit/55e561b))
### 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))
## 2.0.0-pre.1 (2024-01-29)
## [1.5.0-develop.1](https://github.com/chenasraf/simple-scaffold/compare/v1.4.0...v1.5.0-develop.1) (2023-05-02)
* chore: fix docs & formatting ([b4f0731](https://github.com/chenasraf/simple-scaffold/commit/b4f0731))
* chore: update deps ([22ad5d4](https://github.com/chenasraf/simple-scaffold/commit/22ad5d4))
* chore: update deps ([b2373aa](https://github.com/chenasraf/simple-scaffold/commit/b2373aa))
* chore(release): 2.0.0-pre.1 [skip ci] ([add794a](https://github.com/chenasraf/simple-scaffold/commit/add794a))
* ci: fix ([29a7aa3](https://github.com/chenasraf/simple-scaffold/commit/29a7aa3))
* ci: fix docs ([d4cb767](https://github.com/chenasraf/simple-scaffold/commit/d4cb767))
* ci: fix docs ([570b00d](https://github.com/chenasraf/simple-scaffold/commit/570b00d))
* ci: fix docs ([7ef3421](https://github.com/chenasraf/simple-scaffold/commit/7ef3421))
* ci: fix docs build dir ([96c1d5a](https://github.com/chenasraf/simple-scaffold/commit/96c1d5a))
* ci: fix docs command ([5cf5692](https://github.com/chenasraf/simple-scaffold/commit/5cf5692))
* ci: update build ([d168dc1](https://github.com/chenasraf/simple-scaffold/commit/d168dc1))
* ci: update docs build ([9830df8](https://github.com/chenasraf/simple-scaffold/commit/9830df8))
* ci: use tag versions ([b6fed83](https://github.com/chenasraf/simple-scaffold/commit/b6fed83))
* docs: docusaurus initial commit ([a955c4d](https://github.com/chenasraf/simple-scaffold/commit/a955c4d))
* docs: gtag + update deps ([e0c0f5c](https://github.com/chenasraf/simple-scaffold/commit/e0c0f5c))
* docs: update ([4821be6](https://github.com/chenasraf/simple-scaffold/commit/4821be6))
* docs: update ([c95477d](https://github.com/chenasraf/simple-scaffold/commit/c95477d))
* docs: update docs ([f59111f](https://github.com/chenasraf/simple-scaffold/commit/f59111f))
* docs: update docs ([3b0fc7a](https://github.com/chenasraf/simple-scaffold/commit/3b0fc7a))
* docs: update docs, remove generated files from git ([f0a080c](https://github.com/chenasraf/simple-scaffold/commit/f0a080c))
* docs: update readme image ([8478d36](https://github.com/chenasraf/simple-scaffold/commit/8478d36))
* fix: remove gh flag ([e66d6ba](https://github.com/chenasraf/simple-scaffold/commit/e66d6ba))
* feat: try multiple default config files ([f25cda7](https://github.com/chenasraf/simple-scaffold/commit/f25cda7))
* chore!: remove `Name` from default data ([0bb282c](https://github.com/chenasraf/simple-scaffold/commit/0bb282c))
* chore!: update massarg ([55877f0](https://github.com/chenasraf/simple-scaffold/commit/55877f0))
* feat!: remove url colon syntax ([b57be8e](https://github.com/chenasraf/simple-scaffold/commit/b57be8e))
* feat!: rename verbose to logLevel ([17fdf0c](https://github.com/chenasraf/simple-scaffold/commit/17fdf0c))
* feat!: separate git/github/config flags ([995b433](https://github.com/chenasraf/simple-scaffold/commit/995b433))
### 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.9.0 (2024-01-02)
## [1.4.0](https://github.com/chenasraf/simple-scaffold/compare/v1.3.2...v1.4.0) (2023-04-28)
* chore: update dependencies ([758719d](https://github.com/chenasraf/simple-scaffold/commit/758719d))
* chore(release): 1.9.0 [skip ci] ([4d1a6e1](https://github.com/chenasraf/simple-scaffold/commit/4d1a6e1))
* ci: fix actions ([daae9c1](https://github.com/chenasraf/simple-scaffold/commit/daae9c1))
* ci: update build process ([e26fe2a](https://github.com/chenasraf/simple-scaffold/commit/e26fe2a))
* ci: update build steps ([1903055](https://github.com/chenasraf/simple-scaffold/commit/1903055))
* ci: update docs build, semantic release ([7e1acf0](https://github.com/chenasraf/simple-scaffold/commit/7e1acf0))
* feat: add --recurse-submodules to git clone ([cbaf130](https://github.com/chenasraf/simple-scaffold/commit/cbaf130))
### Features
* add `--key` | `-k` to config loader ([6c5ba0b](https://github.com/chenasraf/simple-scaffold/commit/6c5ba0bc916fb1d59240d2eaa1abedc74527a974))
## 1.8.0 (2023-11-29)
## [1.3.2](https://github.com/chenasraf/simple-scaffold/compare/v1.3.1...v1.3.2) (2023-04-28)
* chore: update dependencies ([b048841](https://github.com/chenasraf/simple-scaffold/commit/b048841))
* chore(release): 1.8.0 [skip ci] ([f666c35](https://github.com/chenasraf/simple-scaffold/commit/f666c35)), closes [#63](https://github.com/chenasraf/simple-scaffold/issues/63)
* chore(release): 1.8.0-pre.1 [skip ci] ([746f924](https://github.com/chenasraf/simple-scaffold/commit/746f924)), closes [#63](https://github.com/chenasraf/simple-scaffold/issues/63)
* docs: update configuration files docs ([f5d55f2](https://github.com/chenasraf/simple-scaffold/commit/f5d55f2))
* ci: update release config ([807c3e2](https://github.com/chenasraf/simple-scaffold/commit/807c3e2))
* fix(config): fn config load ([457c904](https://github.com/chenasraf/simple-scaffold/commit/457c904)), closes [#63](https://github.com/chenasraf/simple-scaffold/issues/63)
* build(deps-dev): bump @babel/traverse from 7.21.5 to 7.23.2 ([0fa1ad4](https://github.com/chenasraf/simple-scaffold/commit/0fa1ad4))
### Bug Fixes
* release build ([2c23fa9](https://github.com/chenasraf/simple-scaffold/commit/2c23fa9dbb310cd0a31f09606798f96b95d66779))
* release build asset ([0bef2df](https://github.com/chenasraf/simple-scaffold/commit/0bef2df5f3aa800ad5f1094c0996108db9acce51))
## <small>1.7.2 (2023-08-20)</small>
## [1.3.1](https://github.com/chenasraf/simple-scaffold/compare/v1.3.0...v1.3.1) (2023-04-28)
* chore(release): 1.7.2 [skip ci] ([d62eeeb](https://github.com/chenasraf/simple-scaffold/commit/d62eeeb))
### 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))
## <small>1.7.2-pre.1 (2023-08-15)</small>
## [1.3.0](https://github.com/chenasraf/simple-scaffold/compare/v1.2.0...v1.3.0) (2023-04-25)
* chore: bump version number ([9f5716e](https://github.com/chenasraf/simple-scaffold/commit/9f5716e))
* chore: formatting ([8c3369a](https://github.com/chenasraf/simple-scaffold/commit/8c3369a))
* chore: update dependencies ([d2a2fda](https://github.com/chenasraf/simple-scaffold/commit/d2a2fda))
* chore: update logs ([20ef0ce](https://github.com/chenasraf/simple-scaffold/commit/20ef0ce))
* chore(release): 1.7.2-pre.1 [skip ci] ([9f58fff](https://github.com/chenasraf/simple-scaffold/commit/9f58fff))
* ci: trigger on pre branch ([dbba81d](https://github.com/chenasraf/simple-scaffold/commit/dbba81d))
* fix: windows path resolution ([98ee000](https://github.com/chenasraf/simple-scaffold/commit/98ee000))
* test: add tests ([3413151](https://github.com/chenasraf/simple-scaffold/commit/3413151))
### Features
- load scaffold config from files
([c398976](https://github.com/chenasraf/simple-scaffold/commit/c3989769fee445c9183ff5e5b3892c4e9fb66a9e))
### Bug Fixes
## <small>1.7.1 (2023-06-07)</small>
- 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))
* chore(release): 1.7.1 [skip ci] ([de05bca](https://github.com/chenasraf/simple-scaffold/commit/de05bca))
* build: fix tsconfig ([2cf31e8](https://github.com/chenasraf/simple-scaffold/commit/2cf31e8))
* build: update release rules, add tests ([3714e8b](https://github.com/chenasraf/simple-scaffold/commit/3714e8b))
## [1.2.0](https://github.com/chenasraf/simple-scaffold/compare/v1.1.4...v1.2.0) (2023-04-24)
### Features
- append-data cli flag
([3c5c2de](https://github.com/chenasraf/simple-scaffold/commit/3c5c2ded02f61ff086e81ea4a7f40529bdff1c9d))
## <small>1.7.1-develop.1 (2023-06-07)</small>
### Bug Fixes
* chore(release): 1.7.1-develop.1 [skip ci] ([77be7c0](https://github.com/chenasraf/simple-scaffold/commit/77be7c0))
- 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
## 1.7.0 (2023-05-17)
- github action node version
([7c19c53](https://github.com/chenasraf/simple-scaffold/commit/7c19c533376dc6904231e5cc51c7a4b2658c66e0))
- github action node version
([94fec76](https://github.com/chenasraf/simple-scaffold/commit/94fec766165f7540c578dbf2d0aeeb6ea3969ad8))
* chore(release): 1.7.0 [skip ci] ([4868925](https://github.com/chenasraf/simple-scaffold/commit/4868925))
### 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)
## 1.7.0-develop.7 (2023-06-07)
### Bug Fixes
* chore(release): 1.7.0-develop.7 [skip ci] ([fea6c0f](https://github.com/chenasraf/simple-scaffold/commit/fea6c0f))
* fix: local config file load error ([2b74239](https://github.com/chenasraf/simple-scaffold/commit/2b74239))
- base path
([943717a](https://github.com/chenasraf/simple-scaffold/commit/943717a76998ec0609f2072c886df6b4775f2ea2))
- binary files + add tests
([e450ad2](https://github.com/chenasraf/simple-scaffold/commit/e450ad242ed70ae928b19964da38cdcb1b6cf659))
## [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.7.0-develop.6 (2023-05-27)
## [1.0.1-pre.1](https://github.com/chenasraf/simple-scaffold/compare/v1.0.0...v1.0.1-pre.1) (2022-02-17)
* chore(release): 1.7.0-develop.6 [skip ci] ([a47ba11](https://github.com/chenasraf/simple-scaffold/commit/a47ba11))
* docs: update README.md ([06b1552](https://github.com/chenasraf/simple-scaffold/commit/06b1552))
* test: add tests ([4f27b7b](https://github.com/chenasraf/simple-scaffold/commit/4f27b7b))
## [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.3](https://github.com/chenasraf/simple-scaffold/compare/v0.7.2...v0.7.3) (2021-09-26)
## 1.7.0 (2023-05-17)
## [0.7.2](https://github.com/chenasraf/simple-scaffold/compare/v0.6.1...v0.7.2) (2021-04-19)
* chore(release): 1.7.0 [skip ci] ([4868925](https://github.com/chenasraf/simple-scaffold/commit/4868925))
## [0.6.1](https://github.com/chenasraf/simple-scaffold/compare/v0.6.0...v0.6.1) (2021-02-01)
### Bug Fixes
- binary files
([7c0c347](https://github.com/chenasraf/simple-scaffold/commit/7c0c3470020d7c166ea68a8effa6df65ec38f2c8))
## 1.7.0-develop.5 (2023-05-12)
## [0.6.0](https://github.com/chenasraf/simple-scaffold/compare/v0.5.0...v0.6.0) (2021-02-01)
* chore(release): 1.7.0-develop.5 [skip ci] ([bee430a](https://github.com/chenasraf/simple-scaffold/commit/bee430a))
* test: fix + add tests ([3dfc920](https://github.com/chenasraf/simple-scaffold/commit/3dfc920))
* refactor: remove lodash dependency ([68307d1](https://github.com/chenasraf/simple-scaffold/commit/68307d1))
* build: update workflows ([33e1d56](https://github.com/chenasraf/simple-scaffold/commit/33e1d56))
### Bug Fixes
- support deeper file structure
([4afafa5](https://github.com/chenasraf/simple-scaffold/commit/4afafa5a4af2e3f4b0af54f20811ecb2c8d98560))
## [0.5.0](https://github.com/chenasraf/simple-scaffold/compare/v0.4.5...v0.5.0) (2019-02-27)
## 1.7.0-develop.4 (2023-05-11)
## [0.4.5](https://github.com/chenasraf/simple-scaffold/compare/v0.4.4...v0.4.5) (2019-02-27)
* chore(release): 1.7.0-develop.4 [skip ci] ([c446439](https://github.com/chenasraf/simple-scaffold/commit/c446439))
* build: update package asset name ([773fd00](https://github.com/chenasraf/simple-scaffold/commit/773fd00))
## [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.2](https://github.com/chenasraf/simple-scaffold/compare/v0.4.1...v0.4.2) (2019-02-25)
## 1.7.0-develop.3 (2023-05-11)
## [0.4.1](https://github.com/chenasraf/simple-scaffold/compare/v0.3.1...v0.4.1) (2019-02-25)
* chore: cleanup ([9393c54](https://github.com/chenasraf/simple-scaffold/commit/9393c54))
* chore(release): 1.7.0-develop.3 [skip ci] ([22763f6](https://github.com/chenasraf/simple-scaffold/commit/22763f6))
* build: add packageManager key to package.json ([dac5527](https://github.com/chenasraf/simple-scaffold/commit/dac5527))
* build: update package link ([a54b1f9](https://github.com/chenasraf/simple-scaffold/commit/a54b1f9))
* build: update workflows ([339459c](https://github.com/chenasraf/simple-scaffold/commit/339459c))
* build: update workflows ([62b4a1c](https://github.com/chenasraf/simple-scaffold/commit/62b4a1c))
* build: use pnpm ([5844c08](https://github.com/chenasraf/simple-scaffold/commit/5844c08))
## [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)
## 1.7.0-develop.2 (2023-05-10)
## [0.1.5](https://github.com/chenasraf/simple-scaffold/compare/v0.1.4...v0.1.5) (2018-01-01)
* chore(release): 1.7.0-develop.2 [skip ci] ([263bf0b](https://github.com/chenasraf/simple-scaffold/commit/263bf0b))
* build: update release.config.js ([e0ed371](https://github.com/chenasraf/simple-scaffold/commit/e0ed371))
## [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.2 (2018-01-01)
## 1.7.0-develop.1 (2023-05-09)
* chore: cleanups ([c027e37](https://github.com/chenasraf/simple-scaffold/commit/c027e37))
* chore: cleanups ([2251a9c](https://github.com/chenasraf/simple-scaffold/commit/2251a9c))
* chore(release): 1.7.0-develop.1 [skip ci] ([87934fb](https://github.com/chenasraf/simple-scaffold/commit/87934fb))
* build: fix build ([19b7ed5](https://github.com/chenasraf/simple-scaffold/commit/19b7ed5))
* feat: function config file ([02a8ba1](https://github.com/chenasraf/simple-scaffold/commit/02a8ba1))
* fix: use path.normalize ([565090a](https://github.com/chenasraf/simple-scaffold/commit/565090a))
* docs: update docs ([943aad1](https://github.com/chenasraf/simple-scaffold/commit/943aad1))
* docs: update readme + author ([be92047](https://github.com/chenasraf/simple-scaffold/commit/be92047))
* test: move scaffold.config.js ([9fb4762](https://github.com/chenasraf/simple-scaffold/commit/9fb4762))
## 1.6.0 (2023-05-05)
* chore(release): 1.6.0 [skip ci] ([0940e84](https://github.com/chenasraf/simple-scaffold/commit/0940e84))
* chore(release): bump version number ([77e477e](https://github.com/chenasraf/simple-scaffold/commit/77e477e))
* build: fix package.json version update ([6c8eb02](https://github.com/chenasraf/simple-scaffold/commit/6c8eb02))
* build: use pnpm instead of yarn ([08b0488](https://github.com/chenasraf/simple-scaffold/commit/08b0488))
* docs: fix changelog title ([5ba6034](https://github.com/chenasraf/simple-scaffold/commit/5ba6034))
* docs: update docs ([95dafdf](https://github.com/chenasraf/simple-scaffold/commit/95dafdf))
## 1.6.0-develop.1 (2023-05-04)
* chore(release): 1.5.0-develop.2 [skip ci] ([81743f1](https://github.com/chenasraf/simple-scaffold/commit/81743f1))
* chore(release): 1.5.0-develop.3 [skip ci] ([4bf9674](https://github.com/chenasraf/simple-scaffold/commit/4bf9674))
* chore(release): 1.6.0-develop.1 [skip ci] ([4c3f1c9](https://github.com/chenasraf/simple-scaffold/commit/4c3f1c9))
* docs: fix cli.md page ([2ca91a7](https://github.com/chenasraf/simple-scaffold/commit/2ca91a7))
* docs: fix link ([ba984b6](https://github.com/chenasraf/simple-scaffold/commit/ba984b6))
* docs: update docs ([260ce6a](https://github.com/chenasraf/simple-scaffold/commit/260ce6a))
* docs: update docs ([7b6260c](https://github.com/chenasraf/simple-scaffold/commit/7b6260c))
* docs: update package description ([1799971](https://github.com/chenasraf/simple-scaffold/commit/1799971))
* feat: node.js function for remote configs ([ce5adbe](https://github.com/chenasraf/simple-scaffold/commit/ce5adbe))
* build: remove unnecessary dependency ([99318f7](https://github.com/chenasraf/simple-scaffold/commit/99318f7))
* build: separate test & build ([9489f14](https://github.com/chenasraf/simple-scaffold/commit/9489f14))
* fix: move dependency to dev dependency ([d916d88](https://github.com/chenasraf/simple-scaffold/commit/d916d88))
## 1.5.0 (2023-05-02)
* chore: bump version number ([7f10db0](https://github.com/chenasraf/simple-scaffold/commit/7f10db0))
* chore(release): 1.5.0 [skip ci] ([7be79fd](https://github.com/chenasraf/simple-scaffold/commit/7be79fd))
* docs: update help text ([1a3fd3d](https://github.com/chenasraf/simple-scaffold/commit/1a3fd3d))
## 1.5.0-develop.1 (2023-05-02)
* chore: fix package version ([10ea6b4](https://github.com/chenasraf/simple-scaffold/commit/10ea6b4))
* chore(release): 1.5.0-develop.1 [skip ci] ([93f5b4a](https://github.com/chenasraf/simple-scaffold/commit/93f5b4a))
* docs: add docs for remote templates ([b74411d](https://github.com/chenasraf/simple-scaffold/commit/b74411d))
* feat: add github remote templates ([f961c13](https://github.com/chenasraf/simple-scaffold/commit/f961c13))
* feat: support for remote template configs ([05487f4](https://github.com/chenasraf/simple-scaffold/commit/05487f4))
* build: always build docs ([ce39918](https://github.com/chenasraf/simple-scaffold/commit/ce39918))
* build: fix git/github step order ([c50518a](https://github.com/chenasraf/simple-scaffold/commit/c50518a))
## 1.4.0 (2023-04-28)
* chore(release): 1.4.0 [skip ci] ([83d3807](https://github.com/chenasraf/simple-scaffold/commit/83d3807))
* feat: add `--key` | `-k` to config loader ([6c5ba0b](https://github.com/chenasraf/simple-scaffold/commit/6c5ba0b))
## <small>1.3.2 (2023-04-28)</small>
* chore(release): 1.3.2 [skip ci] ([2c4eccd](https://github.com/chenasraf/simple-scaffold/commit/2c4eccd))
* fix: release build ([2c23fa9](https://github.com/chenasraf/simple-scaffold/commit/2c23fa9))
* fix: release build asset ([0bef2df](https://github.com/chenasraf/simple-scaffold/commit/0bef2df))
## <small>1.3.1 (2023-04-28)</small>
* chore: bump version number + fix changelog ([eba7897](https://github.com/chenasraf/simple-scaffold/commit/eba7897))
* chore(release): 1.3.1 [skip ci] ([398a5d7](https://github.com/chenasraf/simple-scaffold/commit/398a5d7))
* fix: docs ([6e19a86](https://github.com/chenasraf/simple-scaffold/commit/6e19a86))
* fix: remove old peer-dep ([c7e2ef8](https://github.com/chenasraf/simple-scaffold/commit/c7e2ef8))
* docs: fix doc links ([36dd27e](https://github.com/chenasraf/simple-scaffold/commit/36dd27e))
## 1.3.0 (2023-04-25)
* chore(release): 1.3.0 [skip ci] ([dfdbca5](https://github.com/chenasraf/simple-scaffold/commit/dfdbca5))
* docs: add changelog to typedoc ([873fa77](https://github.com/chenasraf/simple-scaffold/commit/873fa77))
* docs: add table of contents ([8a2207b](https://github.com/chenasraf/simple-scaffold/commit/8a2207b))
* docs: clean up css ([1d6643b](https://github.com/chenasraf/simple-scaffold/commit/1d6643b))
* docs: fix CHANGELOG.md ([0c8e6e7](https://github.com/chenasraf/simple-scaffold/commit/0c8e6e7))
* docs: move migration to pages, fix urls ([1460868](https://github.com/chenasraf/simple-scaffold/commit/1460868))
* docs: remove unnecessary nested menus ([42568e0](https://github.com/chenasraf/simple-scaffold/commit/42568e0))
* docs: reorganize file structure ([f41ebfb](https://github.com/chenasraf/simple-scaffold/commit/f41ebfb))
* docs: split into files ([a4498f9](https://github.com/chenasraf/simple-scaffold/commit/a4498f9))
* docs: update config doc ([5a2b187](https://github.com/chenasraf/simple-scaffold/commit/5a2b187))
* docs: update readme ([f28280e](https://github.com/chenasraf/simple-scaffold/commit/f28280e))
* docs: use js for typedoc config ([9b86499](https://github.com/chenasraf/simple-scaffold/commit/9b86499))
* build: only generate docs on master ([1e0b731](https://github.com/chenasraf/simple-scaffold/commit/1e0b731))
* build: remove unnecessary yarn pack ([b3f7912](https://github.com/chenasraf/simple-scaffold/commit/b3f7912))
* build: update changelog sections ([1ce4a41](https://github.com/chenasraf/simple-scaffold/commit/1ce4a41))
* fix: config option should not be mandatory ([3db6a91](https://github.com/chenasraf/simple-scaffold/commit/3db6a91))
* fix: export config file type ([4302eb5](https://github.com/chenasraf/simple-scaffold/commit/4302eb5))
* feat: load scaffold config from files ([c398976](https://github.com/chenasraf/simple-scaffold/commit/c398976))
## 1.2.0 (2023-04-24)
* chore: bump version number ([8e432bf](https://github.com/chenasraf/simple-scaffold/commit/8e432bf))
* chore: bump version number [skip-ci] ([029f260](https://github.com/chenasraf/simple-scaffold/commit/029f260))
* chore: update dependencies ([20400bd](https://github.com/chenasraf/simple-scaffold/commit/20400bd))
* chore: update FUNDING.yml ([1bfcafa](https://github.com/chenasraf/simple-scaffold/commit/1bfcafa))
* chore(release): 1.2.0 [skip ci] ([7da786a](https://github.com/chenasraf/simple-scaffold/commit/7da786a))
* fix: ci node version ([767d34c](https://github.com/chenasraf/simple-scaffold/commit/767d34c))
* fix: github action node version ([7c19c53](https://github.com/chenasraf/simple-scaffold/commit/7c19c53))
* fix: github action node version ([94fec76](https://github.com/chenasraf/simple-scaffold/commit/94fec76))
* fix: semantic-release build dir ([f7956dd](https://github.com/chenasraf/simple-scaffold/commit/f7956dd))
* fix: support quote wrapping in append-data ([4fecca8](https://github.com/chenasraf/simple-scaffold/commit/4fecca8))
* build: add missing dependencies ([75641e5](https://github.com/chenasraf/simple-scaffold/commit/75641e5))
* build: add missing dependencies ([f4c745b](https://github.com/chenasraf/simple-scaffold/commit/f4c745b))
* build: fix build ([47b4c42](https://github.com/chenasraf/simple-scaffold/commit/47b4c42))
* build: fix docs build ([7a4c0ab](https://github.com/chenasraf/simple-scaffold/commit/7a4c0ab))
* build: semantic-release ([2050ea3](https://github.com/chenasraf/simple-scaffold/commit/2050ea3))
* build: update dependencies & fix build ([59a46b0](https://github.com/chenasraf/simple-scaffold/commit/59a46b0))
* build: update github action versions ([222e1a0](https://github.com/chenasraf/simple-scaffold/commit/222e1a0))
* docs: update docs ([7ef6d58](https://github.com/chenasraf/simple-scaffold/commit/7ef6d58))
* docs: update domain ([8f5bee8](https://github.com/chenasraf/simple-scaffold/commit/8f5bee8))
* docs: update spacing ([ed385ec](https://github.com/chenasraf/simple-scaffold/commit/ed385ec))
* docs: update table css ([833ea9d](https://github.com/chenasraf/simple-scaffold/commit/833ea9d))
* docs: update typedoc & remove custom theme ([8fb508f](https://github.com/chenasraf/simple-scaffold/commit/8fb508f))
* docs: update typedoc version ([c334396](https://github.com/chenasraf/simple-scaffold/commit/c334396))
* feat: append-data cli flag ([3c5c2de](https://github.com/chenasraf/simple-scaffold/commit/3c5c2de))
* Bump json5 from 2.2.0 to 2.2.3 ([e28c4db](https://github.com/chenasraf/simple-scaffold/commit/e28c4db))
* Bump minimatch from 3.0.4 to 3.1.2 ([ee4e52c](https://github.com/chenasraf/simple-scaffold/commit/ee4e52c))
## <small>1.1.3 (2023-03-11)</small>
* chore: bump version number [publish] ([d7d2b13](https://github.com/chenasraf/simple-scaffold/commit/d7d2b13))
* fix: base path ([943717a](https://github.com/chenasraf/simple-scaffold/commit/943717a))
* fix: binary files + add tests ([e450ad2](https://github.com/chenasraf/simple-scaffold/commit/e450ad2))
* add gaid to docs ([9bd6219](https://github.com/chenasraf/simple-scaffold/commit/9bd6219))
* fix build ([0364247](https://github.com/chenasraf/simple-scaffold/commit/0364247))
* fix build ([ac2c0d7](https://github.com/chenasraf/simple-scaffold/commit/ac2c0d7))
* fix doc deps ([12f8bca](https://github.com/chenasraf/simple-scaffold/commit/12f8bca))
* fix docs build ([0042c12](https://github.com/chenasraf/simple-scaffold/commit/0042c12))
* fix docs build process ([35262b5](https://github.com/chenasraf/simple-scaffold/commit/35262b5))
* fix gtag ([643431d](https://github.com/chenasraf/simple-scaffold/commit/643431d))
* fix workflow [skip publish] ([0d359b1](https://github.com/chenasraf/simple-scaffold/commit/0d359b1))
* formatting updates ([b569f2b](https://github.com/chenasraf/simple-scaffold/commit/b569f2b))
* improve docs build process ([3b77e69](https://github.com/chenasraf/simple-scaffold/commit/3b77e69))
* improve docs build process ([3cf9359](https://github.com/chenasraf/simple-scaffold/commit/3cf9359))
* improve docs build process ([8957b59](https://github.com/chenasraf/simple-scaffold/commit/8957b59))
* npm audit fix ([5571aba](https://github.com/chenasraf/simple-scaffold/commit/5571aba))
* remove unnecessary install ([11edb0d](https://github.com/chenasraf/simple-scaffold/commit/11edb0d))
* revert docs build in ci ([ac8af8e](https://github.com/chenasraf/simple-scaffold/commit/ac8af8e))
* try fix docs build process ([96b93d8](https://github.com/chenasraf/simple-scaffold/commit/96b93d8))
* try to fix docs [skip publish] ([af65eca](https://github.com/chenasraf/simple-scaffold/commit/af65eca))
* Typdoc (#37) [skip publish] ([f2a75c9](https://github.com/chenasraf/simple-scaffold/commit/f2a75c9)), closes [#37](https://github.com/chenasraf/simple-scaffold/issues/37)
* update docs ([a002402](https://github.com/chenasraf/simple-scaffold/commit/a002402))
* update docs ([27a6ba4](https://github.com/chenasraf/simple-scaffold/commit/27a6ba4))
* update docs ([923b531](https://github.com/chenasraf/simple-scaffold/commit/923b531))
* update docs ([bf10fb8](https://github.com/chenasraf/simple-scaffold/commit/bf10fb8))
* update docs [skip publish] ([4e2fa01](https://github.com/chenasraf/simple-scaffold/commit/4e2fa01))
* update docs + formatting update ([2841ebd](https://github.com/chenasraf/simple-scaffold/commit/2841ebd))
* update docs + spell checker ([7c69010](https://github.com/chenasraf/simple-scaffold/commit/7c69010))
* update docs GA ([869911b](https://github.com/chenasraf/simple-scaffold/commit/869911b))
* Update FUNDING.yml ([a8e9f71](https://github.com/chenasraf/simple-scaffold/commit/a8e9f71))
* update help command ([8c8cede](https://github.com/chenasraf/simple-scaffold/commit/8c8cede))
* update intro gif ([1b37a8e](https://github.com/chenasraf/simple-scaffold/commit/1b37a8e))
* update intro gif ([576798c](https://github.com/chenasraf/simple-scaffold/commit/576798c))
* Update README.md ([5308ef9](https://github.com/chenasraf/simple-scaffold/commit/5308ef9))
* Update README.md ([6fa2a8b](https://github.com/chenasraf/simple-scaffold/commit/6fa2a8b))
* Update README.md ([adba649](https://github.com/chenasraf/simple-scaffold/commit/adba649))
* Update README.md ([cd68ab4](https://github.com/chenasraf/simple-scaffold/commit/cd68ab4))
* Update README.md ([bdc23e7](https://github.com/chenasraf/simple-scaffold/commit/bdc23e7))
* Update README.md ([6160a04](https://github.com/chenasraf/simple-scaffold/commit/6160a04))
* update test desc ([40606ed](https://github.com/chenasraf/simple-scaffold/commit/40606ed))
* docs: fix typo ([1d0c20c](https://github.com/chenasraf/simple-scaffold/commit/1d0c20c))
## 1.1.0 (2022-04-21)
* Add keywords to package.json [skip ci] ([4a4e024](https://github.com/chenasraf/simple-scaffold/commit/4a4e024))
* Bump minimist from 1.2.5 to 1.2.6 ([56be5f3](https://github.com/chenasraf/simple-scaffold/commit/56be5f3))
* bump version number ([dffa81f](https://github.com/chenasraf/simple-scaffold/commit/dffa81f))
* update package.json ([3c57638](https://github.com/chenasraf/simple-scaffold/commit/3c57638))
* Update README.md [skip ci] ([86a7a2c](https://github.com/chenasraf/simple-scaffold/commit/86a7a2c))
* Update README.md [skip ci] ([d3259c4](https://github.com/chenasraf/simple-scaffold/commit/d3259c4))
* v1.1 (#35) ([a3deda2](https://github.com/chenasraf/simple-scaffold/commit/a3deda2)), closes [#35](https://github.com/chenasraf/simple-scaffold/issues/35)
* chore: use rimraf + add error debug log [skip ci] ([b2799d0](https://github.com/chenasraf/simple-scaffold/commit/b2799d0))
* docs: Update README.md [skip ci] ([f4997c6](https://github.com/chenasraf/simple-scaffold/commit/f4997c6))
## <small>1.0.3 (2022-03-03)</small>
* bump version number ([cb6e06f](https://github.com/chenasraf/simple-scaffold/commit/cb6e06f))
* bump version number [skip ci] ([21c4ab6](https://github.com/chenasraf/simple-scaffold/commit/21c4ab6))
* fix transform of windows-style paths ([56f1340](https://github.com/chenasraf/simple-scaffold/commit/56f1340))
* fixed more windows paths, updated tests ([52cb3e7](https://github.com/chenasraf/simple-scaffold/commit/52cb3e7))
* import/file cleanup ([d6e1693](https://github.com/chenasraf/simple-scaffold/commit/d6e1693))
* improved test ([f07df79](https://github.com/chenasraf/simple-scaffold/commit/f07df79))
* refactor handlebarsParse - remove redundant arg ([89d7897](https://github.com/chenasraf/simple-scaffold/commit/89d7897))
* remove unnecessary package [skip ci] ([1783ddf](https://github.com/chenasraf/simple-scaffold/commit/1783ddf))
* update README.md ([e26a434](https://github.com/chenasraf/simple-scaffold/commit/e26a434))
* updated tests ([a043a05](https://github.com/chenasraf/simple-scaffold/commit/a043a05))
## <small>1.0.1-pre.1 (2022-02-17)</small>
* add build step ([f1698d2](https://github.com/chenasraf/simple-scaffold/commit/f1698d2))
* add cmd args ([7cdf5e4](https://github.com/chenasraf/simple-scaffold/commit/7cdf5e4))
* add dry run option ([aeddd44](https://github.com/chenasraf/simple-scaffold/commit/aeddd44))
* add export for cmd_util ([6b57406](https://github.com/chenasraf/simple-scaffold/commit/6b57406))
* add subFolderNameHelper arg ([81ba5f5](https://github.com/chenasraf/simple-scaffold/commit/81ba5f5))
* add tests ([c42a58c](https://github.com/chenasraf/simple-scaffold/commit/c42a58c))
* added --quiet flag ([4f81654](https://github.com/chenasraf/simple-scaffold/commit/4f81654))
* added custom helpers ([d03d0e0](https://github.com/chenasraf/simple-scaffold/commit/d03d0e0))
* bump alpha version number ([d797e5b](https://github.com/chenasraf/simple-scaffold/commit/d797e5b))
* Bump ansi-regex from 5.0.0 to 5.0.1 ([36f8b87](https://github.com/chenasraf/simple-scaffold/commit/36f8b87))
* bump version number ([5ab2637](https://github.com/chenasraf/simple-scaffold/commit/5ab2637))
* bump version number ([53e8bc4](https://github.com/chenasraf/simple-scaffold/commit/53e8bc4))
* bump version: v1.0.0 ([d06c0d6](https://github.com/chenasraf/simple-scaffold/commit/d06c0d6))
* code splitting ([208ee30](https://github.com/chenasraf/simple-scaffold/commit/208ee30))
* Create FUNDING.yml ([40b5920](https://github.com/chenasraf/simple-scaffold/commit/40b5920))
* fail handlebars parse silently ([0af6392](https://github.com/chenasraf/simple-scaffold/commit/0af6392))
* fix basename in some cases ([b1b1aca](https://github.com/chenasraf/simple-scaffold/commit/b1b1aca))
* fix build output files ([99c9055](https://github.com/chenasraf/simple-scaffold/commit/99c9055))
* fix build/publish cmd ([a59f29d](https://github.com/chenasraf/simple-scaffold/commit/a59f29d))
* fix cmd ([cd34930](https://github.com/chenasraf/simple-scaffold/commit/cd34930))
* fix copyright ([54b9023](https://github.com/chenasraf/simple-scaffold/commit/54b9023))
* fix errors, fix nested output ([8413225](https://github.com/chenasraf/simple-scaffold/commit/8413225))
* fix log level 0 ([84e6207](https://github.com/chenasraf/simple-scaffold/commit/84e6207))
* fix main field in package.json ([7273538](https://github.com/chenasraf/simple-scaffold/commit/7273538))
* fix readme [skip publish] ([ad30ee0](https://github.com/chenasraf/simple-scaffold/commit/ad30ee0))
* fix yarn.lock ([a21a35f](https://github.com/chenasraf/simple-scaffold/commit/a21a35f))
* fixed release tarball file location ([3f2945e](https://github.com/chenasraf/simple-scaffold/commit/3f2945e))
* fixed release tarball file location ([9303446](https://github.com/chenasraf/simple-scaffold/commit/9303446))
* fixes + add log level [skip publish] ([2623b78](https://github.com/chenasraf/simple-scaffold/commit/2623b78))
* helpers fix ([f4cc44c](https://github.com/chenasraf/simple-scaffold/commit/f4cc44c))
* improve tests ([2d5626c](https://github.com/chenasraf/simple-scaffold/commit/2d5626c))
* maintain directory structure ([564e821](https://github.com/chenasraf/simple-scaffold/commit/564e821))
* major refactor ([5483490](https://github.com/chenasraf/simple-scaffold/commit/5483490))
* migrate cmd to massarg + update tests ([a52f9a0](https://github.com/chenasraf/simple-scaffold/commit/a52f9a0))
* refactoring - code cleanup ([c3835a7](https://github.com/chenasraf/simple-scaffold/commit/c3835a7))
* remove excess files ([d0c0152](https://github.com/chenasraf/simple-scaffold/commit/d0c0152))
* remove types from package.json ([559b5ad](https://github.com/chenasraf/simple-scaffold/commit/559b5ad))
* run tests on ci [skip publish] ([2305083](https://github.com/chenasraf/simple-scaffold/commit/2305083))
* support node 12 for fs package ([bc224d9](https://github.com/chenasraf/simple-scaffold/commit/bc224d9))
* support node 12 for fs package ([cf923d8](https://github.com/chenasraf/simple-scaffold/commit/cf923d8))
* try fix release upload ([8575b1e](https://github.com/chenasraf/simple-scaffold/commit/8575b1e))
* try fix workflow ([6f03ed9](https://github.com/chenasraf/simple-scaffold/commit/6f03ed9))
* try fix workflow ([474a3dc](https://github.com/chenasraf/simple-scaffold/commit/474a3dc))
* try new release version ([d8aba21](https://github.com/chenasraf/simple-scaffold/commit/d8aba21))
* try to fix workflow ([c17e630](https://github.com/chenasraf/simple-scaffold/commit/c17e630))
* update alpha workflow ([91116bb](https://github.com/chenasraf/simple-scaffold/commit/91116bb))
* update deps + add MIGRATION.md ([5b72b6c](https://github.com/chenasraf/simple-scaffold/commit/5b72b6c))
* update deps + update cmd requirements ([d96992c](https://github.com/chenasraf/simple-scaffold/commit/d96992c))
* update docs [skip publish] ([c2bc8b7](https://github.com/chenasraf/simple-scaffold/commit/c2bc8b7))
* update jest config ([01e458e](https://github.com/chenasraf/simple-scaffold/commit/01e458e))
* Update MIGRATION.md [skip ci] ([d0a0db0](https://github.com/chenasraf/simple-scaffold/commit/d0a0db0))
* update readme [skip ci] ([9259939](https://github.com/chenasraf/simple-scaffold/commit/9259939))
* update readme [skip ci] ([09403e1](https://github.com/chenasraf/simple-scaffold/commit/09403e1))
* Update README.md ([edcf1ac](https://github.com/chenasraf/simple-scaffold/commit/edcf1ac))
* update README.md, default output fix ([391a08a](https://github.com/chenasraf/simple-scaffold/commit/391a08a))
* update workflow ([27e84d1](https://github.com/chenasraf/simple-scaffold/commit/27e84d1))
* update workflows ([c7749a8](https://github.com/chenasraf/simple-scaffold/commit/c7749a8))
* update workflows ([a6f25fa](https://github.com/chenasraf/simple-scaffold/commit/a6f25fa))
* update workflows [skip publish] ([956b007](https://github.com/chenasraf/simple-scaffold/commit/956b007))
* update workflows [skip publish] ([54848f9](https://github.com/chenasraf/simple-scaffold/commit/54848f9))
* use node 12 ([9385371](https://github.com/chenasraf/simple-scaffold/commit/9385371))
* v0.7.4 ([43b6496](https://github.com/chenasraf/simple-scaffold/commit/43b6496))
* publish: debug mode off, try to fix workflow ([535260a](https://github.com/chenasraf/simple-scaffold/commit/535260a))
* publish: debug mode on ([1498857](https://github.com/chenasraf/simple-scaffold/commit/1498857))
* build: add workflow ([0ce19a7](https://github.com/chenasraf/simple-scaffold/commit/0ce19a7))
* build: update workflow ([3ee66b2](https://github.com/chenasraf/simple-scaffold/commit/3ee66b2))
* chore: cleanup ([8fcc7a6](https://github.com/chenasraf/simple-scaffold/commit/8fcc7a6))
* docs: update README ([b4b0de6](https://github.com/chenasraf/simple-scaffold/commit/b4b0de6))
## <small>0.7.5 (2021-09-26)</small>
* fix main field in package.json ([3cb9a6f](https://github.com/chenasraf/simple-scaffold/commit/3cb9a6f))
## <small>0.7.4 (2021-09-26)</small>
* v0.7.4 ([12974b5](https://github.com/chenasraf/simple-scaffold/commit/12974b5))
## <small>0.7.3 (2021-09-26)</small>
* added --quiet flag ([7f98d46](https://github.com/chenasraf/simple-scaffold/commit/7f98d46))
* Bump handlebars from 4.7.6 to 4.7.7 ([552614c](https://github.com/chenasraf/simple-scaffold/commit/552614c))
* Bump hosted-git-info from 2.8.8 to 2.8.9 ([2e12907](https://github.com/chenasraf/simple-scaffold/commit/2e12907))
* Bump lodash from 4.17.20 to 4.17.21 ([5b7e0e3](https://github.com/chenasraf/simple-scaffold/commit/5b7e0e3))
* Bump url-parse from 1.4.7 to 1.5.1 ([0923830](https://github.com/chenasraf/simple-scaffold/commit/0923830))
* update readme ([813f706](https://github.com/chenasraf/simple-scaffold/commit/813f706))
* update readme ([1bc2221](https://github.com/chenasraf/simple-scaffold/commit/1bc2221))
* Update README.md ([cd25b04](https://github.com/chenasraf/simple-scaffold/commit/cd25b04))
## <small>0.7.2 (2021-04-19)</small>
* add basename to output config function (fixes #3) ([f07affa](https://github.com/chenasraf/simple-scaffold/commit/f07affa)), closes [#3](https://github.com/chenasraf/simple-scaffold/issues/3)
* disable overwriting files + parse JSON for locals ([ce22a2c](https://github.com/chenasraf/simple-scaffold/commit/ce22a2c))
## <small>0.6.1 (2021-02-01)</small>
* fix: binary files ([7c0c347](https://github.com/chenasraf/simple-scaffold/commit/7c0c347))
## 0.6.0 (2021-02-01)
* build: upgrade packages ([977288a](https://github.com/chenasraf/simple-scaffold/commit/977288a))
* fix: support deeper file structure ([4afafa5](https://github.com/chenasraf/simple-scaffold/commit/4afafa5))
* 0.5.0 ([7bee2a5](https://github.com/chenasraf/simple-scaffold/commit/7bee2a5))
## 0.5.0 (2019-02-27)
* Fixed output argument + updated README ([06590c4](https://github.com/chenasraf/simple-scaffold/commit/06590c4))
* v0.5.0 ([d4c049b](https://github.com/chenasraf/simple-scaffold/commit/d4c049b))
## <small>0.4.5 (2019-02-27)</small>
* Improved docs ([a410b79](https://github.com/chenasraf/simple-scaffold/commit/a410b79))
* v0.4.5 ([c4f2dfb](https://github.com/chenasraf/simple-scaffold/commit/c4f2dfb))
## <small>0.4.4 (2019-02-27)</small>
* v0.4.4 ([71d544a](https://github.com/chenasraf/simple-scaffold/commit/71d544a))
## <small>0.4.3 (2019-02-27)</small>
* mapfile ([d7a4362](https://github.com/chenasraf/simple-scaffold/commit/d7a4362))
* v0.4.3 ([20389d7](https://github.com/chenasraf/simple-scaffold/commit/20389d7))
## <small>0.4.2 (2019-02-25)</small>
* bugfixes ([a92c471](https://github.com/chenasraf/simple-scaffold/commit/a92c471))
* mapfile ([07b1c4b](https://github.com/chenasraf/simple-scaffold/commit/07b1c4b))
* v0.4.2 ([0a2d7c0](https://github.com/chenasraf/simple-scaffold/commit/0a2d7c0))
## <small>0.4.1 (2019-02-25)</small>
* added 'createSubFolder' option, cleaned up CMD file ([d6195c6](https://github.com/chenasraf/simple-scaffold/commit/d6195c6))
* Update README.md ([b14e3d2](https://github.com/chenasraf/simple-scaffold/commit/b14e3d2))
* v0.4.1 ([ec91fbf](https://github.com/chenasraf/simple-scaffold/commit/ec91fbf))
* Bugfix: dotfiles ([85aa9f9](https://github.com/chenasraf/simple-scaffold/commit/85aa9f9))
## <small>0.3.1 (2018-01-15)</small>
* Update README.md ([686b0bf](https://github.com/chenasraf/simple-scaffold/commit/686b0bf))
* v0.3.1 ([fa2ddca](https://github.com/chenasraf/simple-scaffold/commit/fa2ddca))
## 0.3.0 (2018-01-15)
* cleanups ([4f29a61](https://github.com/chenasraf/simple-scaffold/commit/4f29a61))
* output is optional ([14b60ff](https://github.com/chenasraf/simple-scaffold/commit/14b60ff))
* Rename ([45e8de3](https://github.com/chenasraf/simple-scaffold/commit/45e8de3))
* Uodate README.md ([b09299b](https://github.com/chenasraf/simple-scaffold/commit/b09299b))
* Update README.md ([1275743](https://github.com/chenasraf/simple-scaffold/commit/1275743))
* Update README.md ([a3a77e2](https://github.com/chenasraf/simple-scaffold/commit/a3a77e2))
* v0.3.0 ([0be29dd](https://github.com/chenasraf/simple-scaffold/commit/0be29dd))
## 0.2.0 (2018-01-05)
* Fixed cmd ([4ca7c6a](https://github.com/chenasraf/simple-scaffold/commit/4ca7c6a))
* Improve build ([0fd9964](https://github.com/chenasraf/simple-scaffold/commit/0fd9964))
* Improve cmd script, add readme ([e391f8f](https://github.com/chenasraf/simple-scaffold/commit/e391f8f))
* Move all scripts to webpack, add wip cmd script for bin ([3e42ac5](https://github.com/chenasraf/simple-scaffold/commit/3e42ac5))
* Use handlebars, add cmd script in package.json ([e64c0e4](https://github.com/chenasraf/simple-scaffold/commit/e64c0e4))
* v0.2.0 ([f360159](https://github.com/chenasraf/simple-scaffold/commit/f360159))
## <small>0.1.5 (2018-01-01)</small>
* v0.1.5 ([eecec82](https://github.com/chenasraf/simple-scaffold/commit/eecec82))
## <small>0.1.4 (2018-01-01)</small>
* v0.1.4 ([6ec19fc](https://github.com/chenasraf/simple-scaffold/commit/6ec19fc))
## <small>0.1.3 (2018-01-01)</small>
* v0.1.3 ([a5776d6](https://github.com/chenasraf/simple-scaffold/commit/a5776d6))
## <small>0.1.2 (2018-01-01)</small>
* Get comp name from argv ([c341fe7](https://github.com/chenasraf/simple-scaffold/commit/c341fe7))
* Initial commit ([4896c10](https://github.com/chenasraf/simple-scaffold/commit/4896c10))
* Published + renamed, bugfixes ([1e0abf9](https://github.com/chenasraf/simple-scaffold/commit/1e0abf9))
* Remove dist from gitignore ([7f9a385](https://github.com/chenasraf/simple-scaffold/commit/7f9a385))
* Scaffold basically works, file path resolving sucky atm ([652621f](https://github.com/chenasraf/simple-scaffold/commit/652621f))
* v0.1.2 ([0fed899](https://github.com/chenasraf/simple-scaffold/commit/0fed899))

124
README.md
View File

@@ -6,7 +6,7 @@
[Documentation](https://chenasraf.github.io/simple-scaffold) |
[NPM](https://npmjs.com/package/simple-scaffold) | [casraf.dev](https://casraf.dev)
![version](https://img.shields.io/github/package-json/v/chenasraf/simple-scaffold/master?label=version)
![master](https://img.shields.io/github/package-json/v/chenasraf/simple-scaffold/master?label=master)
![build](https://img.shields.io/github/actions/workflow/status/chenasraf/simple-scaffold/release.yml?branch=master)
</h2>
@@ -25,7 +25,7 @@ lifting for you and start building your projects faster and more efficiently tod
<div align="center">
![sample animation](media/intro.gif)
![Intro](https://chenasraf.github.io/simple-scaffold/img/intro.gif)
</div>
@@ -40,10 +40,12 @@ The fastest way to get started is to use `npx` to immediately start a scaffold p
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:
Simple Scaffold will maintain any file and directory structure you try to generate.
`templates/component/{{ pascalName name }}.tsx`
```tsx
// Created: {{ now | 'yyyy-MM-dd' }}
// Created: {{ now 'yyyy-MM-dd' }}
import React from 'react'
export default {{pascalCase name}}: React.FC = (props) => {
@@ -55,16 +57,16 @@ export default {{pascalCase name}}: React.FC = (props) => {
To generate the template output, run:
```shell
```sh
# generate single component
npx simple-scaffold@latest \
$ npx simple-scaffold@latest \
-t templates/component -o src/components PageWrapper
```
This will immediately create the following file: `src/components/PageWrapper.tsx`
```tsx
// Created: 2077/01/01
// Created: 2077-01-01
import React from 'react'
export default PageWrapper: React.FC = (props) => {
@@ -74,37 +76,89 @@ export default PageWrapper: React.FC = (props) => {
}
```
### Remote Templates
### Configuration Files
You can also use a config file to more easily maintain all your scaffold definitions.
`scaffold.config.js`
```js
module.exports = {
// use "default" to avoid needing to specify key
// in this case the key is "component"
component: {
templates: ["templates/component"],
output: "src/components",
data: {
// ...
},
},
}
```
Then call your scaffold like this:
```sh
$ npx simple-scaffold@latest -c scaffold.config.js PageWrapper
```
This will allow you to avoid needing to remember which configs are needed or to store them in a
1-liner in `packqge.json` which can get pretty long and messy, which is harder to maintain.
Also, this allows you to define more complex scaffolds with logic without having to use the Node.js
API directly. (Of course you always have the option to still do so if you wish)
See more at the [CLI documentation](https://chenasraf.github.io/simple-scaffold/docs/usage/cli) and
[Configuration Files](https://chenasraf.github.io/simple-scaffold/docs/usage/configuration_files).
### Remote Configurations
Another quick way to start is to re-use someone else's (or your own) work using a template
repository.
A remote config can be loaded in one of these ways:
- If it's on GitHub, you can use `-g user/repository_name`
- If it's on another git server (such as GitLab), you can use
`-g https://example.com/user/repository_name.git`
Configurations can hold multiple scaffold groups. Each group can be accessed using its key by
supplying the `--key` or `-k` argument, like so:
```sh
-g user/repository_name -c scaffold.js -k key_name`.
```
Here is an example for loading the example component templates in this very repository:
```shell
npx simple-scaffold@latest \
-gh chenasraf/simple-scaffold#examples/test-input/scaffold.config.js:component \
```sh
$ npx simple-scaffold@latest \
-g chenasraf/simple-scaffold \
-k component \
PageWrapper
# equivalent to:
npx simple-scaffold@latest \
-c https://github.com/chenasraf/simple-scaffold.git#examples/test-input/scaffold.config.js:component \
$ npx simple-scaffold@latest \
-g https://github.com/chenasraf/simple-scaffold.git \
-c scaffold.config.js \
-k component \
PageWrapper
```
When template name (`:component`) is omitted, `default` is used.
When template name (`-k component`) is omitted, `default` is used.
See more at the [CLI documentation](https://chenasraf.github.io/simple-scaffold/pages/cli.html)
See more at the [CLI documentation](https://chenasraf.github.io/simple-scaffold/docs/usage/cli) and
[Configuration Files](https://chenasraf.github.io/simple-scaffold/docs/usage/configuration_files).
## Documentation
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)
- [Command Line Interface (CLI) usage](https://chenasraf.github.io/simple-scaffold/docs/usage/cli)
- [Node.js usage](https://chenasraf.github.io/simple-scaffold/docs/usage/node)
- [Templates](https://chenasraf.github.io/simple-scaffold/docs/usage/templates)
- [Configuration Files](https://chenasraf.github.io/simple-scaffold/docs/usage/configuration_files)
- [Migration](https://chenasraf.github.io/simple-scaffold/docs/usage/migration)
## Contributing
@@ -113,9 +167,11 @@ very helpful to sustaining its life. If you are feeling incredibly generous and
just a small amount to help sustain this project, I would be very very thankful!
<a href='https://ko-fi.com/casraf' target='_blank'>
<img height='36' style='border:0px;height:36px;'
<img
height='36'
src='https://cdn.ko-fi.com/cdn/kofi1.png?v=3'
alt='Buy Me a Coffee at ko-fi.com' />
alt='Buy Me a Coffee at ko-fi.com'
/>
</a>
I welcome any issues or pull requests on GitHub. If you find a bug, or would like a new feature,
@@ -124,8 +180,8 @@ don't hesitate to open an appropriate issue and I will do my best to reply promp
If you are a developer and want to contribute code, here are some starting tips:
1. Fork this repository
2. Run `yarn install`
3. Run `yarn dev` to start file watch mode
2. Run `pnpm install`
3. Run `pnpm dev` to start file watch mode
4. Make any changes you would like
5. Create tests for your changes
6. Update the relevant documentation (readme, code comments, type comments)
@@ -133,22 +189,22 @@ If you are a developer and want to contribute code, here are some starting tips:
Some tips on getting around the code:
- Use `yarn dev` for development - it runs TypeScript compile in watch mode, allowing you to make
changes and immediately be able to try them using `yarn cmd`.
- Use `yarn build` to build the output once
- Use `yarn test` to run tests
- Use `yarn cmd` to use the CLI feature of Simple Scaffold from within the root directory, enabling
you to test different behaviors. See `yarn cmd -h` for more information.
- Use `pnpm dev` for development - it runs TypeScript compile in watch mode, allowing you to make
changes and immediately be able to try them using `pnpm cmd`.
- Use `pnpm build` to build the output once
- Use `pnpm test` to run tests
- Use `pnpm cmd` to use the CLI feature of Simple Scaffold from within the root directory, enabling
you to test different behaviors. See `pnpm cmd -h` for more information.
> This requires an updated build, and does not trigger one itself. From here you have several
> options:
>
> - Run `yarn dev` to watch for file changes and build automatically
> - Run `yarn build` before running this to trigger a one-time build
> - Run `yarn build-cmd` which triggers a build right before running `yarn cmd` automatically with
> - Run `pnpm dev` to watch for file changes and build automatically
> - Run `pnpm build` before running this to trigger a one-time build
> - Run `pnpm build-cmd` which triggers a build right before running `pnpm cmd` automatically with
> the rest of the given arguments.
- Use `yarn build-docs` to build the documentation once
- Use `yarn watch-docs` to start docs in watch mode
- Use `pnpm build-docs` to build the documentation once
- Use `pnpm watch-docs` to start docs in watch mode
- To see the documentation, currently you have to serve the directory yourself with a static web
server (like node's built in serve, VS code's "Go Live" mode, etc)

22
docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,22 @@
# Dependencies
/node_modules
# Production
/build
# Generated files
.docusaurus
.cache-loader
# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
docs/api

44
docs/README.md Normal file
View File

@@ -0,0 +1,44 @@
# Website
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
### Installation
```
$ yarn
```
### Local Development
```
$ yarn start
```
This command starts a local development server and opens up a browser window. Most changes are
reflected live without having to restart the server.
### Build
```
$ yarn build
```
This command generates static content into the `build` directory and can be served using any static
contents hosting service.
### Deployment
Using SSH:
```
$ USE_SSH=true yarn deploy
```
Not using SSH:
```
$ GIT_USER=<Your GitHub username> yarn deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and
push to the `gh-pages` branch.

3
docs/babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve("@docusaurus/core/lib/babel/preset")],
}

View File

@@ -0,0 +1 @@
label: "Usage"

89
docs/docs/usage/cli.md Normal file
View File

@@ -0,0 +1,89 @@
---
title: CLI Usage
---
## Available flags
```text
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 |
## Examples:
> See
> [Configuration Files](https://chenasraf.github.io/simple-scaffold/docs/usage/configuration_files)
> for organizing multiple scaffold types into easy-to-maintain files
Usage with config file
```shell
$ simple-scaffold -c scaffold.cmd.js -k component MyComponent
```
Usage with GitHub config file
```shell
$ simple-scaffold -g chenasraf/simple-scaffold -k component MyComponent
```
Usage with https git URL (for non-GitHub)
```shell
$ simple-scaffold \
-g https://example.com/user/template.git \
-c scaffold.cmd.js \
-k component \
MyComponent
```
Full syntax with config path and template key (applicable to all above methods)
```shell
$ simple-scaffold -c scaffold.cmd.js -k component MyComponent
```
Excluded template key, assumes 'default' key
```shell
$ simple-scaffold -c scaffold.cmd.js MyComponent
```
Shortest syntax for GitHub, assumes file 'scaffold.cmd.js' and template key 'default'
```shell
$ simple-scaffold -g chenasraf/simple-scaffold MyComponent
```
You can also add this as a script in your `package.json`:
```json
{
"scripts": {
"scaffold-cfg": "npx simple-scaffold -c scaffold.cmd.js -k component",
"scaffold-gh": "npx simple-scaffold -g chenasraf/simple-scaffold -k component",
"scaffold": "npx simple-scaffold@latest -t scaffolds/component/**/* -o src/components -d '{\"myProp\": \"propName\", \"myVal\": 123}'"
"scaffold-component": "npx simple-scaffold -c scaffold.cmd.js -k"
}
}
```

View File

@@ -0,0 +1,184 @@
---
title: Configuration Files
---
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`/`.mjs`/`.cjs`/`.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:
```js
module.exports = {
component: {
templates: ["templates/component"],
output: "src/components",
},
}
```
The configuration contents are identical to the
[Node.js configuration structure](https://chenasraf.github.io/simple-scaffold/docs/usage/node):
```ts
interface ScaffoldConfig {
name: string
templates: string[]
output: FileResponse<string>
subdir?: boolean
git?: string
config?: string
key?: string
data?: Record<string, any>
overwrite?: FileResponse<boolean>
quiet?: boolean
verbose?: LogLevel
dryRun?: boolean
helpers?: Record<string, Helper>
subdirHelper?: 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`/`.cjs`/`.mjs` file
as JSON does not support non-primitives.
A `.js` file can be 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",
},
}
```
Another feature of using a JS file is you can export a function which will be loaded with the CMD
config provided to Simple Scaffold. The `extra` key contains any values not consumed by built-in
flags, so you can pre-process your args before outputting a config:
```js
/** @type {import('simple-scaffold').ScaffoldConfigFile} */
module.exports = (config) => {
console.log("Config:", config)
return {
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 alongside `--key` or `-k`, denoting the key to use as the config object, as
you define in your config:
```sh
simple-scaffold -c <file> -k <template_key>
```
For example:
```sh
simple-scaffold -c scaffold.json -k component MyComponentName
```
If you don't want to supply a template/config name (e.g. `component`), `default` will be used:
```js
/** @type {import('simple-scaffold').ScaffoldConfigFile} */
module.exports = {
default: {
// ...
},
}
```
And then:
```sh
# will use 'default' template
simple-scaffold -c scaffold.json MyComponentName
```
- When the filename is omitted, the following files will be tried in order:
- `scaffold.config.*`
- `scaffold.*`
Where `*` denotes any supported file extension, in the priority listed in
[Supported file types](#supported-file-types)
- When the `template_key` is ommitted, `default` will be used as default.
### Supported file types
Any importable file is supported, depending on your build process.
Common files include:
- `*.mjs`
- `*.cjs`
- `*.js`
- `*.json`
When filenames are ommited when loading configs, these are the file extensions that will be
automatically tried, by the specified order of priority.
Note that you might need to find the correct extension of `.js`, `.cjs` or `.mjs` depending on your
build process and your package type (for example, packages with `"type": "module"` in their
`package.json` might be required to use `.mjs`.)
### Git/GitHub Templates
You may specify a git or GitHub url to use remote templates.
The command line option is `--git` or `-g`.
- You may specify a full git or HTTPS git URL, which will be tried
- You may specify a git username and project if the project is on GitHub
```sh
# GitHub shorthand
simple-scaffold -g <username>/<project_name> [-c <filename>] [-k <template_key>]
# Any git URL, git:// and https:// are supported
simple-scaffold -g git://gitlab.com/<username>/<project_name> [-c <filename>] [-k <template_key>]
simple-scaffold -g https://gitlab.com/<username>/<project_name>.git [-c <filename>] [-k <template_key>]
```
## Use In Node.js
You can also start a scaffold from Node.js with a remote file or URL config.
Just use the `Scaffold.fromConfig` function:
```ts
Scaffold.fromConfig(
"scaffold.config.js", // file or HTTPS git URL
{
// name of the generated component
name: "My Component",
// key to load from the config
key: "component",
},
{
// other config overrides
},
)
```

141
docs/docs/usage/examples.md Normal file
View File

@@ -0,0 +1,141 @@
---
title: Examples
---
## Example 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 `subdir = false` (default):
```text
project → src → components → MyComponent.js
```
- With `subdir = true`:
```text
project → src → components → MyComponent → MyComponent.js
```
- With `subdir = true` and `subdirHelper = 'upperCase'`:
```text
project → src → components → MYCOMPONENT → MyComponent.js
```
- Output file contents:
```typescript
/**
* Author: My Name
* Date: 2077-01-01
*/
import React from 'react'
export default MyComponent: React.FC = (props) => {
return (
<div className="myClassName">MyComponent Component</div>
)
}
```
## Example run commands
### Command Example
```bash
simple-scaffold \
-t project/scaffold/**/* \
-o src/components \
-d '{"className": "myClassName","author": "My Name"}'
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: "My Name",
},
})
console.log("Done.")
}
```
### Re-usable config
#### Shell
```bash
# cjs
simple-scaffold -c scaffold.cjs MyComponent \
-d '{"className": "myClassName","author": "My Name"}'
# mjs
simple-scaffold -c scaffold.mjs MyComponent \
-d '{"className": "myClassName","author": "My Name"}'
```
#### scaffold.cjs
```js
module.exports = (config) => ({
default: {
templates: ["project/scaffold/**/*"],
output: ["src/components"],
data: {
className: "myClassName",
author: "My Name",
},
},
})
```
#### scaffold.mjs
```js
export default (config) => ({
default: {
templates: ["project/scaffold/**/*"],
output: ["src/components"],
data: {
className: "myClassName",
author: "My Name",
},
},
})
```

10
docs/docs/usage/index.md Normal file
View File

@@ -0,0 +1,10 @@
---
title: Usage
---
- [CLI Usage](cli)
- [Configuration Files](configuration_files)
- [Examples](examples)
- [Migration](migration)
- [Node.js Usage](node)
- [Template Files](templates)

View File

@@ -0,0 +1,62 @@
---
title: Migration
---
## v1.x to v2.x
### CLI option changes
- Several changes to how remote configs are loaded via CLI:
- The `:template_key` syntax has been removed. You can still use `-k template_key` to achieve the
same result.
- The `--github` (`-gh`) flag has been replaced by a generic `--git` (`-g`) one, which handles any
git URL. Providing a partial GitHub path will default to trying to find the project on GitHub,
e.g. `-g username/project`
- The `#template_file` syntax has been removed, you may use `--config` or `-c` to tell Simple
Scaffold which file to look for inside the git project. There is a default file priority list
which can find the file for you if it is in one of the supported filenames.
- `verbose` can now take the names `debug`, `info`, `warn`, `error` or `none` (case insensitive) or
as usual by using the numbering from before.
- `--create-sub-folder` (`-s`) has been renamed to `--subdir` (`-s`) in the CLI. The Node.js names
have been changed as well.
- `--sub-folder-name-helper` (`-sh`) has been renamed to `--subdir-helper` (`-sh`). The Node.js
names have been changed as well.
- All boolean flags no longer take a value. `-q` instead of `-q 1` or `-q true`, `-s` instead of
`-s 1`, `-w` instead of `-w 1`, etc.
### Behavior changes
- Data is no longer auto-populated with `Name` (PascalCase) by default. You can just use the helper
in your templates contents and file names, simply use `{{ pascalCase name }}` instead of
`{{ Name }}`. `Name` was arbitrary and it is confusing (is it `Title Case`? `PascalCase`? only
reading the docs can tell). Alternatively, you can inject the transformed name into your `data`
manually using a scaffold config file, by using the Node API or by appending the data to the CLI
invocation.
## v0.x to v1.x
In Simple Scaffold v1.0, the entire codebase was overhauled, yet usage remains mostly the same
between versions. With these notable exceptions:
- Some of the argument names have changed
- Template syntax has been improved
- The command to run Scaffold has been simplified from `new SimpleScaffold(opts).run()` to
`SimpleScaffold(opts)`, which now returns a promise that you can await to know when the process
has been completed.
### Argument changes
- `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
[Command Line Interface (CLI) usage](https://chenasraf.github.io/simple-scaffold/docs/usage/cli)
and [Node.js usage](https://chenasraf.github.io/simple-scaffold/docs/usage/node) for more
information.
### Template syntax changes
Simple Scaffold still uses Handlebars.js to handle template content and file names. However, helpers
have been added to remove the need for you to pre-process the template data on simple use-cases such
as case type manipulation (converting to camel case, snake case, etc)
See the readme for the full information on how to use these helpers and which are available.

View File

@@ -1,3 +1,7 @@
---
title: Node.js Usage
---
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.

View File

@@ -1,3 +1,7 @@
---
title: Template Files
---
# Preparing template files
Put your template files anywhere, and fill them with tokens for replacement.
@@ -121,104 +125,7 @@ config.helpers = {
```
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.
`subdirHelper` (`--sub-dir-helper`/`-H`) 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>
)
}
```

184
docs/docusaurus.config.ts Normal file
View File

@@ -0,0 +1,184 @@
import { themes as prismThemes } from "prism-react-renderer"
import type { Config } from "@docusaurus/types"
import type * as Preset from "@docusaurus/preset-classic"
const config: Config = {
title: "Simple Scaffold",
tagline: "Generate any file structure - from single components to entire app boilerplates, with a single command.",
favicon: "img/favicon.ico",
// Set the production url of your site here
url: "https://chenasraf.github.io",
// Set the /<baseUrl>/ pathname under which your site is served
// For GitHub pages deployment, it is often '/<projectName>/'
baseUrl: "/simple-scaffold",
// GitHub pages deployment config.
// If you aren't using GitHub pages, you don't need these.
organizationName: "chenasraf", // Usually your GitHub org/user name.
projectName: "simple-scaffold", // Usually your repo name.
onBrokenLinks: "warn",
onBrokenMarkdownLinks: "warn",
// Even if you don't use internationalization, you can use this field to set
// useful metadata like html lang. For example, if your site is Chinese, you
// may want to replace "en" with "zh-Hans".
i18n: {
defaultLocale: "en",
locales: ["en"],
},
plugins: [
[
"docusaurus-plugin-typedoc",
// Plugin / TypeDoc options
{
entryPoints: ["../src/index.ts"],
tsconfig: "../tsconfig.json",
// typedoc options
watch: process.env.NODE_ENV === "development",
excludePrivate: true,
excludeProtected: true,
excludeInternal: true,
// includeVersion: true,
categorizeByGroup: false,
sort: ["visibility"],
categoryOrder: ["Main", "*"],
media: "media",
entryPointStrategy: "expand",
validation: {
invalidLink: true,
},
},
],
],
presets: [
[
"classic",
{
docs: {
sidebarPath: "./sidebars.ts",
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl: "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/",
},
blog: {
showReadingTime: true,
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl: "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/",
},
theme: {
customCss: "./src/css/custom.css",
},
googleTagManager: {
containerId: "GTM-KHQS9TQ",
},
} satisfies Preset.Options,
],
],
themeConfig: {
// Replace with your project's social card
image: "img/docusaurus-social-card.jpg",
navbar: {
title: "Simple Scaffold",
logo: {
alt: "Simple Scaffold",
src: "img/logo.svg",
},
items: [
{
position: "left",
type: "docSidebar",
sidebarId: "api",
label: "API",
to: "docs/api",
},
{
position: "left",
type: "docSidebar",
sidebarId: "usage",
label: "Usage",
to: "docs/usage",
},
// {
// position: "left",
// type: "docSidebar",
// sidebarId: "docs",
// },
// {
// label: "API",
// href: "/docs/api",
// position: "left",
// },
// {
// label: "Usage",
// href: "/docs/usage",
// position: "left",
// },
{
href: "https://npmjs.com/package/simple-scaffold",
label: "NPM",
position: "right",
},
{
href: "https://github.com/facebook/docusaurus",
label: "GitHub",
position: "right",
},
],
},
footer: {
style: "dark",
links: [
{
title: "Docs",
items: [
{
label: "Tutorial",
to: "/docs/intro",
},
],
},
{
title: "More from @casraf",
items: [
{
label: "Massarg - CLI Argument Parser",
href: "https://chenasraf.github.io/massarg",
},
{
label: "Website",
href: "https://casraf.dev",
},
],
},
{
title: "More",
items: [
{
label: "npm",
href: "https://npmjs.com/package/simple-scaffold",
},
{
label: "GitHub",
href: "https://github.com/chenasraf/simple-scaffold",
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} Chen Asraf. Built with Docusaurus.`,
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
},
} satisfies Preset.ThemeConfig,
}
export default config

51
docs/package.json Normal file
View File

@@ -0,0 +1,51 @@
{
"name": "simple-scaffold-docs",
"version": "0.0.0",
"private": true,
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start --port 3001",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
},
"dependencies": {
"@docusaurus/core": "3.1.1",
"@docusaurus/plugin-google-tag-manager": "^3.1.1",
"@docusaurus/preset-classic": "3.1.1",
"@mdx-js/react": "^3.0.0",
"clsx": "^2.1.0",
"prism-react-renderer": "^2.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.1.1",
"@docusaurus/tsconfig": "3.1.1",
"@docusaurus/types": "3.1.1",
"docusaurus-plugin-typedoc": "^0.22.0",
"typedoc": "^0.25.7",
"typedoc-plugin-markdown": "^3.17.1",
"typescript": "~5.2.2"
},
"browserslist": {
"production": [
">0.5%",
"not dead",
"not op_mini all"
],
"development": [
"last 3 chrome version",
"last 3 firefox version",
"last 5 safari version"
]
},
"engines": {
"node": ">=18.0"
}
}

9042
docs/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

51
docs/sidebars.ts Normal file
View File

@@ -0,0 +1,51 @@
import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"
/**
* Creating a sidebar enables you to:
- create an ordered group of docs
- render a sidebar for each doc of that group
- provide next/previous navigation
The sidebars can be generated from the filesystem, or explicitly defined here.
Create as many sidebars as you want.
*/
const sidebars: SidebarsConfig = {
// By default, Docusaurus generates a sidebar from the docs folder structure
// docs: [{ type: "autogenerated", dirName: "." }],
usage: ["usage/index"],
api: ["api/index"],
docs: [{ type: "autogenerated", dirName: "." }],
// docs: [
// {
// type: "category",
// label: "Guides",
// link: {
// type: "generated-index",
// title: "Docusaurus Guides",
// description: "Learn about the most important Docusaurus concepts!",
// slug: "/category/docusaurus-guides",
// keywords: ["guides"],
// image: "/img/docusaurus.png",
// },
// items: ["pages", "docs", "blog", "search"],
// },
// ],
// usage: [{ type: "autogenerated", dirName: "usage" }],
// api: [{ type: "autogenerated", dirName: "api" }],
// But you can create a sidebar manually
/*
tutorialSidebar: [
'intro',
'hello',
{
type: 'category',
label: 'Tutorial',
items: ['tutorial-basics/create-a-document'],
},
],
*/
}
export default sidebars

View File

@@ -0,0 +1,72 @@
import clsx from "clsx"
import Heading from "@theme/Heading"
import styles from "./styles.module.css"
type FeatureItem = {
title: string
Svg: React.ComponentType<React.ComponentProps<"svg">>
description: JSX.Element
}
const FeatureList: FeatureItem[] = [
{
title: "Easy to Use",
Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default,
description: (
<>
Generate anything from a simple component to an entire app boilerplate - you decide! Put dynamic data in your
templates to quickly generate skeletons, formatted data dumps, or repetitive code - and immediately get to
coding!
</>
),
},
{
title: "Use It Anywhere, For Anything",
Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default,
description: (
<>
Whether you need files specific to your project or commonly used templates - you can use them both locally or
use Git to share them with your team. Spackle on some one-time-use data, and run one command.
</>
),
},
{
title: "Handlebars Support",
Svg: require("@site/static/img/undraw_docusaurus_react.svg").default,
description: (
<>
Did you think you stop at some static data? Generate entire mapped lists of items, pre-parse information, fake
data, and more - you can attach any function or any data to your templates. Handlebars will parse it all and
generate the files you need.
</>
),
},
]
function Feature({ title, Svg, description }: FeatureItem) {
return (
<div className={clsx("col col--4")}>
<div className="text--center">
<Svg className={styles.featureSvg} role="img" />
</div>
<div className="text--center padding-horiz--md">
<Heading as="h3">{title}</Heading>
<p>{description}</p>
</div>
</div>
)
}
export default function HomepageFeatures(): JSX.Element {
return (
<section className={styles.features}>
<div className="container">
<div className="row">
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
))}
</div>
</div>
</section>
)
}

View File

@@ -0,0 +1,11 @@
.features {
display: flex;
align-items: center;
padding: 2rem 0;
width: 100%;
}
.featureSvg {
height: 200px;
width: 200px;
}

30
docs/src/css/custom.css Normal file
View File

@@ -0,0 +1,30 @@
/**
* Any CSS included here will be global. The classic template
* bundles Infima by default. Infima is a CSS framework designed to
* work well for content-centric websites.
*/
/* You can override the default Infima variables here. */
:root {
--ifm-color-primary: #2e8555;
--ifm-color-primary-dark: #29784c;
--ifm-color-primary-darker: #277148;
--ifm-color-primary-darkest: #205d3b;
--ifm-color-primary-light: #33925d;
--ifm-color-primary-lighter: #359962;
--ifm-color-primary-lightest: #3cad6e;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
}
/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme="dark"] {
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: #21af90;
--ifm-color-primary-darker: #1fa588;
--ifm-color-primary-darkest: #1a8870;
--ifm-color-primary-light: #29d5b0;
--ifm-color-primary-lighter: #32d8b4;
--ifm-color-primary-lightest: #4fddbf;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}

View File

@@ -0,0 +1,24 @@
/**
* CSS files with the .module.css suffix will be treated as CSS modules
* and scoped locally.
*/
.heroBanner {
padding: 4rem 0;
text-align: center;
position: relative;
overflow: hidden;
}
@media screen and (max-width: 996px) {
.heroBanner {
padding: 2rem;
}
}
.buttons {
display: flex;
align-items: center;
justify-content: center;
gap: 2rem;
}

42
docs/src/pages/index.tsx Normal file
View File

@@ -0,0 +1,42 @@
import clsx from "clsx"
import Link from "@docusaurus/Link"
import useDocusaurusContext from "@docusaurus/useDocusaurusContext"
import Layout from "@theme/Layout"
import HomepageFeatures from "@site/src/components/HomepageFeatures"
import Heading from "@theme/Heading"
import styles from "./index.module.css"
function HomepageHeader() {
const { siteConfig } = useDocusaurusContext()
return (
<header className={clsx("hero hero--primary", styles.heroBanner)}>
<div className="container">
<Heading as="h1" className="hero__title">
{siteConfig.title}
</Heading>
<p className="hero__subtitle">{siteConfig.tagline}</p>
<div className={styles.buttons}>
<Link className="button button--secondary button--lg" to="/docs/api">
API
</Link>
<Link className="button button--secondary button--lg" to="/docs/usage">
Usage
</Link>
</div>
</div>
</header>
)
}
export default function Home(): JSX.Element {
const { siteConfig } = useDocusaurusContext()
return (
<Layout title={siteConfig.title} description="Description will go into a meta tag in <head />">
<HomepageHeader />
<main>
<HomepageFeatures />
</main>
</Layout>
)
}

View File

@@ -0,0 +1,7 @@
---
title: Markdown page example
---
# Markdown page example
You don't need React to write simple standalone pages.

0
docs/static/.nojekyll vendored Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
docs/static/img/docusaurus.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
docs/static/img/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 385 KiB

After

Width:  |  Height:  |  Size: 385 KiB

1
docs/static/img/logo.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,171 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1088" height="687.962" viewBox="0 0 1088 687.962">
<title>Easy to Use</title>
<g id="Group_12" data-name="Group 12" transform="translate(-57 -56)">
<g id="Group_11" data-name="Group 11" transform="translate(57 56)">
<path id="Path_83" data-name="Path 83" d="M1017.81,560.461c-5.27,45.15-16.22,81.4-31.25,110.31-20,38.52-54.21,54.04-84.77,70.28a193.275,193.275,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.282,657.282,0,0,0-104.09-13.16q-14.97-.675-29.97-.67c-15.42.02-293.07,5.29-360.67-131.57-16.69-33.76-28.13-75-32.24-125.27-11.63-142.12,52.29-235.46,134.74-296.47,155.97-115.41,369.76-110.57,523.43,7.88C941.15,276.621,1036.99,396.031,1017.81,560.461Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_84" data-name="Path 84" d="M986.56,670.771c-20,38.52-47.21,64.04-77.77,80.28a193.272,193.272,0,0,1-27.46,11.94c-55.61,19.3-117.85,14.18-166.74,3.99a657.3,657.3,0,0,0-104.09-13.16q-14.97-.675-29.97-.67-23.13.03-46.25,1.72c-100.17,7.36-253.82-6.43-321.42-143.29L382,283.981,444.95,445.6l20.09,51.59,55.37-75.98L549,381.981l130.2,149.27,36.8-81.27L970.78,657.9l14.21,11.59Z" transform="translate(-56 -106.019)" fill="#f2f2f2"/>
<path id="Path_85" data-name="Path 85" d="M302,282.962l26-57,36,83-31-60Z" opacity="0.1"/>
<path id="Path_86" data-name="Path 86" d="M610.5,753.821q-14.97-.675-29.97-.67L465.04,497.191Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<path id="Path_87" data-name="Path 87" d="M464.411,315.191,493,292.962l130,150-132-128Z" opacity="0.1"/>
<path id="Path_88" data-name="Path 88" d="M908.79,751.051a193.265,193.265,0,0,1-27.46,11.94L679.2,531.251Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<circle id="Ellipse_11" data-name="Ellipse 11" cx="3" cy="3" r="3" transform="translate(479 98.962)" fill="#f2f2f2"/>
<circle id="Ellipse_12" data-name="Ellipse 12" cx="3" cy="3" r="3" transform="translate(396 201.962)" fill="#f2f2f2"/>
<circle id="Ellipse_13" data-name="Ellipse 13" cx="2" cy="2" r="2" transform="translate(600 220.962)" fill="#f2f2f2"/>
<circle id="Ellipse_14" data-name="Ellipse 14" cx="2" cy="2" r="2" transform="translate(180 265.962)" fill="#f2f2f2"/>
<circle id="Ellipse_15" data-name="Ellipse 15" cx="2" cy="2" r="2" transform="translate(612 96.962)" fill="#f2f2f2"/>
<circle id="Ellipse_16" data-name="Ellipse 16" cx="2" cy="2" r="2" transform="translate(736 192.962)" fill="#f2f2f2"/>
<circle id="Ellipse_17" data-name="Ellipse 17" cx="2" cy="2" r="2" transform="translate(858 344.962)" fill="#f2f2f2"/>
<path id="Path_89" data-name="Path 89" d="M306,121.222h-2.76v-2.76h-1.48v2.76H299V122.7h2.76v2.759h1.48V122.7H306Z" fill="#f2f2f2"/>
<path id="Path_90" data-name="Path 90" d="M848,424.222h-2.76v-2.76h-1.48v2.76H841V425.7h2.76v2.759h1.48V425.7H848Z" fill="#f2f2f2"/>
<path id="Path_91" data-name="Path 91" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_92" data-name="Path 92" d="M1144,719.981c0,16.569-243.557,74-544,74s-544-57.431-544-74,243.557,14,544,14S1144,703.413,1144,719.981Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<ellipse id="Ellipse_18" data-name="Ellipse 18" cx="544" cy="30" rx="544" ry="30" transform="translate(0 583.962)" fill="#3f3d56"/>
<path id="Path_93" data-name="Path 93" d="M624,677.981c0,33.137-14.775,24-33,24s-33,9.137-33-24,33-96,33-96S624,644.844,624,677.981Z" transform="translate(-56 -106.019)" fill="#ff6584"/>
<path id="Path_94" data-name="Path 94" d="M606,690.66c0,15.062-6.716,10.909-15,10.909s-15,4.153-15-10.909,15-43.636,15-43.636S606,675.6,606,690.66Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<rect id="Rectangle_97" data-name="Rectangle 97" width="92" height="18" rx="9" transform="translate(489 604.962)" fill="#2f2e41"/>
<rect id="Rectangle_98" data-name="Rectangle 98" width="92" height="18" rx="9" transform="translate(489 586.962)" fill="#2f2e41"/>
<path id="Path_95" data-name="Path 95" d="M193,596.547c0,55.343,34.719,100.126,77.626,100.126" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_96" data-name="Path 96" d="M270.626,696.673c0-55.965,38.745-101.251,86.626-101.251" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_97" data-name="Path 97" d="M221.125,601.564c0,52.57,22.14,95.109,49.5,95.109" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_98" data-name="Path 98" d="M270.626,696.673c0-71.511,44.783-129.377,100.126-129.377" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_99" data-name="Path 99" d="M254.3,697.379s11.009-.339,14.326-2.7,16.934-5.183,17.757-1.395,16.544,18.844,4.115,18.945-28.879-1.936-32.19-3.953S254.3,697.379,254.3,697.379Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_100" data-name="Path 100" d="M290.716,710.909c-12.429.1-28.879-1.936-32.19-3.953-2.522-1.536-3.527-7.048-3.863-9.591l-.368.014s.7,8.879,4.009,10.9,19.761,4.053,32.19,3.953c3.588-.029,4.827-1.305,4.759-3.2C294.755,710.174,293.386,710.887,290.716,710.909Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_101" data-name="Path 101" d="M777.429,633.081c0,38.029,23.857,68.8,53.341,68.8" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_102" data-name="Path 102" d="M830.769,701.882c0-38.456,26.623-69.575,59.525-69.575" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_103" data-name="Path 103" d="M796.755,636.528c0,36.124,15.213,65.354,34.014,65.354" transform="translate(-56 -106.019)" fill="#6c63ff"/>
<path id="Path_104" data-name="Path 104" d="M830.769,701.882c0-49.139,30.773-88.9,68.8-88.9" transform="translate(-56 -106.019)" fill="#3f3d56"/>
<path id="Path_105" data-name="Path 105" d="M819.548,702.367s7.565-.233,9.844-1.856,11.636-3.562,12.2-.958,11.368,12.949,2.828,13.018-19.844-1.33-22.119-2.716S819.548,702.367,819.548,702.367Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_106" data-name="Path 106" d="M844.574,711.664c-8.54.069-19.844-1.33-22.119-2.716-1.733-1.056-2.423-4.843-2.654-6.59l-.253.01s.479,6.1,2.755,7.487,13.579,2.785,22.119,2.716c2.465-.02,3.317-.9,3.27-2.2C847.349,711.159,846.409,711.649,844.574,711.664Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_107" data-name="Path 107" d="M949.813,724.718s11.36-1.729,14.5-4.591,16.89-7.488,18.217-3.667,19.494,17.447,6.633,19.107-30.153,1.609-33.835-.065S949.813,724.718,949.813,724.718Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_108" data-name="Path 108" d="M989.228,734.173c-12.86,1.659-30.153,1.609-33.835-.065-2.8-1.275-4.535-6.858-5.2-9.45l-.379.061s1.833,9.109,5.516,10.783,20.975,1.725,33.835.065c3.712-.479,4.836-1.956,4.529-3.906C993.319,732.907,991.991,733.817,989.228,734.173Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_109" data-name="Path 109" d="M670.26,723.9s9.587-1.459,12.237-3.875,14.255-6.32,15.374-3.095,16.452,14.725,5.6,16.125-25.448,1.358-28.555-.055S670.26,723.9,670.26,723.9Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_110" data-name="Path 110" d="M703.524,731.875c-10.853,1.4-25.448,1.358-28.555-.055-2.367-1.076-3.827-5.788-4.39-7.976l-.32.051s1.547,7.687,4.655,9.1,17.7,1.456,28.555.055c3.133-.4,4.081-1.651,3.822-3.3C706.977,730.807,705.856,731.575,703.524,731.875Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_111" data-name="Path 111" d="M178.389,719.109s7.463-1.136,9.527-3.016,11.1-4.92,11.969-2.409,12.808,11.463,4.358,12.553-19.811,1.057-22.23-.043S178.389,719.109,178.389,719.109Z" transform="translate(-56 -106.019)" fill="#a8a8a8"/>
<path id="Path_112" data-name="Path 112" d="M204.285,725.321c-8.449,1.09-19.811,1.057-22.23-.043-1.842-.838-2.979-4.506-3.417-6.209l-.249.04s1.2,5.984,3.624,7.085,13.781,1.133,22.23.043c2.439-.315,3.177-1.285,2.976-2.566C206.973,724.489,206.1,725.087,204.285,725.321Z" transform="translate(-56 -106.019)" opacity="0.2"/>
<path id="Path_113" data-name="Path 113" d="M439.7,707.337c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873,42.118-36.793,93.694-36.793S439.7,677.117,439.7,707.337Z" transform="translate(-56 -106.019)" opacity="0.1"/>
<path id="Path_114" data-name="Path 114" d="M439.7,699.9c0,30.22-42.124,20.873-93.7,20.873s-93.074,9.347-93.074-20.873S295.04,663.1,346.616,663.1,439.7,669.676,439.7,699.9Z" transform="translate(-56 -106.019)" fill="#3f3d56"/>
</g>
<g id="docusaurus_keytar" transform="translate(312.271 493.733)">
<path id="Path_40" data-name="Path 40" d="M99,52h91.791V89.153H99Z" transform="translate(5.904 -14.001)" fill="#fff" fill-rule="evenodd"/>
<path id="Path_41" data-name="Path 41" d="M24.855,163.927A21.828,21.828,0,0,1,5.947,153a21.829,21.829,0,0,0,18.908,32.782H46.71V163.927Z" transform="translate(-3 -4.634)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_42" data-name="Path 42" d="M121.861,61.1l76.514-4.782V45.39A21.854,21.854,0,0,0,176.52,23.535H78.173L75.441,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L64.513,18.8a3.154,3.154,0,0,0-5.464,0l-2.732,4.732L53.586,18.8a3.154,3.154,0,0,0-5.464,0L45.39,23.535c-.024,0-.046,0-.071,0l-4.526-4.525a3.153,3.153,0,0,0-5.276,1.414l-1.5,5.577-5.674-1.521a3.154,3.154,0,0,0-3.863,3.864L26,34.023l-5.575,1.494a3.155,3.155,0,0,0-1.416,5.278l4.526,4.526c0,.023,0,.046,0,.07L18.8,48.122a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,59.05a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,69.977a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,80.9a3.154,3.154,0,0,0,0,5.464L23.535,89.1,18.8,91.832a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,102.76a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,113.687a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,124.615a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,135.542a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,146.469a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,157.4a3.154,3.154,0,0,0,0,5.464l4.732,2.732L18.8,168.324a3.154,3.154,0,0,0,0,5.464l4.732,2.732A21.854,21.854,0,0,0,45.39,198.375H176.52a21.854,21.854,0,0,0,21.855-21.855V89.1l-76.514-4.782a11.632,11.632,0,0,1,0-23.219" transform="translate(-1.681 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_43" data-name="Path 43" d="M143,186.71h32.782V143H143Z" transform="translate(9.984 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_44" data-name="Path 44" d="M196.71,159.855a5.438,5.438,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(10.912 -6.025)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_45" data-name="Path 45" d="M153,124.855h32.782V103H153Z" transform="translate(10.912 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_46" data-name="Path 46" d="M194.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.814,2.814,0,0,0,.349.035" transform="translate(12.767 -9.377)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_47" data-name="Path 47" d="M65.087,56.891a2.732,2.732,0,0,1-2.732-2.732,8.2,8.2,0,0,0-16.391,0,2.732,2.732,0,0,1-5.464,0,13.659,13.659,0,0,1,27.319,0,2.732,2.732,0,0,1-2.732,2.732" transform="translate(0.478 -15.068)" fill-rule="evenodd"/>
<path id="Path_48" data-name="Path 48" d="M103,191.347h65.565a21.854,21.854,0,0,0,21.855-21.855V93H124.855A21.854,21.854,0,0,0,103,114.855Z" transform="translate(6.275 -10.199)" fill="#ffff50" fill-rule="evenodd"/>
<path id="Path_49" data-name="Path 49" d="M173.216,129.787H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0-54.434H118.535a1.093,1.093,0,1,1,0-2.185h54.681a1.093,1.093,0,0,1,0,2.185m0,21.652H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186m0,21.855H118.535a1.093,1.093,0,1,1,0-2.186h54.681a1.093,1.093,0,0,1,0,2.186M189.585,61.611c-.013,0-.024-.007-.037-.005-3.377.115-4.974,3.492-6.384,6.472-1.471,3.114-2.608,5.139-4.473,5.078-2.064-.074-3.244-2.406-4.494-4.874-1.436-2.835-3.075-6.049-6.516-5.929-3.329.114-4.932,3.053-6.346,5.646-1.5,2.762-2.529,4.442-4.5,4.364-2.106-.076-3.225-1.972-4.52-4.167-1.444-2.443-3.112-5.191-6.487-5.1-3.272.113-4.879,2.606-6.3,4.808-1.5,2.328-2.552,3.746-4.551,3.662-2.156-.076-3.27-1.65-4.558-3.472-1.447-2.047-3.077-4.363-6.442-4.251-3.2.109-4.807,2.153-6.224,3.954-1.346,1.709-2.4,3.062-4.621,2.977a1.093,1.093,0,0,0-.079,2.186c3.3.11,4.967-1.967,6.417-3.81,1.286-1.635,2.4-3.045,4.582-3.12,2.1-.09,3.091,1.218,4.584,3.327,1.417,2,3.026,4.277,6.263,4.394,3.391.114,5.022-2.42,6.467-4.663,1.292-2,2.406-3.734,4.535-3.807,1.959-.073,3.026,1.475,4.529,4.022,1.417,2.4,3.023,5.121,6.324,5.241,3.415.118,5.064-2.863,6.5-5.5,1.245-2.282,2.419-4.437,4.5-4.509,1.959-.046,2.981,1.743,4.492,4.732,1.412,2.79,3.013,5.95,6.365,6.071l.185,0c3.348,0,4.937-3.36,6.343-6.331,1.245-2.634,2.423-5.114,4.444-5.216Z" transform="translate(7.109 -13.11)" fill-rule="evenodd"/>
<path id="Path_50" data-name="Path 50" d="M83,186.71h43.71V143H83Z" transform="translate(4.42 -5.561)" fill="#3ecc5f" fill-rule="evenodd"/>
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 109.327, 91.085)">
<rect id="Rectangle_3" data-name="Rectangle 3" width="92.361" height="36.462" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
<g id="Group_2" data-name="Group 2" transform="translate(1.531 23.03)">
<rect id="Rectangle_4" data-name="Rectangle 4" width="5.336" height="5.336" rx="1" transform="translate(16.797 0)" fill="#4a4a4a"/>
<rect id="Rectangle_5" data-name="Rectangle 5" width="5.336" height="5.336" rx="1" transform="translate(23.12 0)" fill="#4a4a4a"/>
<rect id="Rectangle_6" data-name="Rectangle 6" width="5.336" height="5.336" rx="1" transform="translate(29.444 0)" fill="#4a4a4a"/>
<rect id="Rectangle_7" data-name="Rectangle 7" width="5.336" height="5.336" rx="1" transform="translate(35.768 0)" fill="#4a4a4a"/>
<rect id="Rectangle_8" data-name="Rectangle 8" width="5.336" height="5.336" rx="1" transform="translate(42.091 0)" fill="#4a4a4a"/>
<rect id="Rectangle_9" data-name="Rectangle 9" width="5.336" height="5.336" rx="1" transform="translate(48.415 0)" fill="#4a4a4a"/>
<rect id="Rectangle_10" data-name="Rectangle 10" width="5.336" height="5.336" rx="1" transform="translate(54.739 0)" fill="#4a4a4a"/>
<rect id="Rectangle_11" data-name="Rectangle 11" width="5.336" height="5.336" rx="1" transform="translate(61.063 0)" fill="#4a4a4a"/>
<rect id="Rectangle_12" data-name="Rectangle 12" width="5.336" height="5.336" rx="1" transform="translate(67.386 0)" fill="#4a4a4a"/>
<path id="Path_51" data-name="Path 51" d="M1.093,0H14.518a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0ZM75,0H88.426a1.093,1.093,0,0,1,1.093,1.093V4.243a1.093,1.093,0,0,1-1.093,1.093H75a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,75,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_3" data-name="Group 3" transform="translate(1.531 10.261)">
<path id="Path_52" data-name="Path 52" d="M1.093,0H6.218A1.093,1.093,0,0,1,7.31,1.093V4.242A1.093,1.093,0,0,1,6.218,5.335H1.093A1.093,1.093,0,0,1,0,4.242V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_13" data-name="Rectangle 13" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
<rect id="Rectangle_14" data-name="Rectangle 14" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
<rect id="Rectangle_15" data-name="Rectangle 15" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
<rect id="Rectangle_16" data-name="Rectangle 16" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
<rect id="Rectangle_17" data-name="Rectangle 17" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
<rect id="Rectangle_18" data-name="Rectangle 18" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
<rect id="Rectangle_19" data-name="Rectangle 19" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
<rect id="Rectangle_20" data-name="Rectangle 20" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
<rect id="Rectangle_21" data-name="Rectangle 21" width="5.336" height="5.336" rx="1" transform="translate(58.888 0)" fill="#4a4a4a"/>
<rect id="Rectangle_22" data-name="Rectangle 22" width="5.336" height="5.336" rx="1" transform="translate(65.212 0)" fill="#4a4a4a"/>
<rect id="Rectangle_23" data-name="Rectangle 23" width="5.336" height="5.336" rx="1" transform="translate(71.536 0)" fill="#4a4a4a"/>
<rect id="Rectangle_24" data-name="Rectangle 24" width="5.336" height="5.336" rx="1" transform="translate(77.859 0)" fill="#4a4a4a"/>
<rect id="Rectangle_25" data-name="Rectangle 25" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
</g>
<g id="Group_4" data-name="Group 4" transform="translate(91.05 9.546) rotate(180)">
<path id="Path_53" data-name="Path 53" d="M1.093,0H6.219A1.093,1.093,0,0,1,7.312,1.093v3.15A1.093,1.093,0,0,1,6.219,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.093A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_26" data-name="Rectangle 26" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
<rect id="Rectangle_27" data-name="Rectangle 27" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
<rect id="Rectangle_28" data-name="Rectangle 28" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
<rect id="Rectangle_29" data-name="Rectangle 29" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
<rect id="Rectangle_30" data-name="Rectangle 30" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
<rect id="Rectangle_31" data-name="Rectangle 31" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
<rect id="Rectangle_32" data-name="Rectangle 32" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
<rect id="Rectangle_33" data-name="Rectangle 33" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
<rect id="Rectangle_34" data-name="Rectangle 34" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
<rect id="Rectangle_35" data-name="Rectangle 35" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
<rect id="Rectangle_36" data-name="Rectangle 36" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
<rect id="Rectangle_37" data-name="Rectangle 37" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
<rect id="Rectangle_38" data-name="Rectangle 38" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
<rect id="Rectangle_39" data-name="Rectangle 39" width="5.336" height="5.336" rx="1" transform="translate(8.299 0)" fill="#4a4a4a"/>
<rect id="Rectangle_40" data-name="Rectangle 40" width="5.336" height="5.336" rx="1" transform="translate(14.623 0)" fill="#4a4a4a"/>
<rect id="Rectangle_41" data-name="Rectangle 41" width="5.336" height="5.336" rx="1" transform="translate(20.947 0)" fill="#4a4a4a"/>
<rect id="Rectangle_42" data-name="Rectangle 42" width="5.336" height="5.336" rx="1" transform="translate(27.271 0)" fill="#4a4a4a"/>
<rect id="Rectangle_43" data-name="Rectangle 43" width="5.336" height="5.336" rx="1" transform="translate(33.594 0)" fill="#4a4a4a"/>
<rect id="Rectangle_44" data-name="Rectangle 44" width="5.336" height="5.336" rx="1" transform="translate(39.918 0)" fill="#4a4a4a"/>
<rect id="Rectangle_45" data-name="Rectangle 45" width="5.336" height="5.336" rx="1" transform="translate(46.242 0)" fill="#4a4a4a"/>
<rect id="Rectangle_46" data-name="Rectangle 46" width="5.336" height="5.336" rx="1" transform="translate(52.565 0)" fill="#4a4a4a"/>
<rect id="Rectangle_47" data-name="Rectangle 47" width="5.336" height="5.336" rx="1" transform="translate(58.889 0)" fill="#4a4a4a"/>
<rect id="Rectangle_48" data-name="Rectangle 48" width="5.336" height="5.336" rx="1" transform="translate(65.213 0)" fill="#4a4a4a"/>
<rect id="Rectangle_49" data-name="Rectangle 49" width="5.336" height="5.336" rx="1" transform="translate(71.537 0)" fill="#4a4a4a"/>
<rect id="Rectangle_50" data-name="Rectangle 50" width="5.336" height="5.336" rx="1" transform="translate(77.86 0)" fill="#4a4a4a"/>
<rect id="Rectangle_51" data-name="Rectangle 51" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
</g>
<g id="Group_6" data-name="Group 6" transform="translate(1.531 16.584)">
<path id="Path_54" data-name="Path 54" d="M1.093,0h7.3A1.093,1.093,0,0,1,9.485,1.093v3.15A1.093,1.093,0,0,1,8.392,5.336h-7.3A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<g id="Group_5" data-name="Group 5" transform="translate(10.671 0)">
<rect id="Rectangle_52" data-name="Rectangle 52" width="5.336" height="5.336" rx="1" fill="#4a4a4a"/>
<rect id="Rectangle_53" data-name="Rectangle 53" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
<rect id="Rectangle_54" data-name="Rectangle 54" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
<rect id="Rectangle_55" data-name="Rectangle 55" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
<rect id="Rectangle_56" data-name="Rectangle 56" width="5.336" height="5.336" rx="1" transform="translate(25.295 0)" fill="#4a4a4a"/>
<rect id="Rectangle_57" data-name="Rectangle 57" width="5.336" height="5.336" rx="1" transform="translate(31.619 0)" fill="#4a4a4a"/>
<rect id="Rectangle_58" data-name="Rectangle 58" width="5.336" height="5.336" rx="1" transform="translate(37.942 0)" fill="#4a4a4a"/>
<rect id="Rectangle_59" data-name="Rectangle 59" width="5.336" height="5.336" rx="1" transform="translate(44.265 0)" fill="#4a4a4a"/>
<rect id="Rectangle_60" data-name="Rectangle 60" width="5.336" height="5.336" rx="1" transform="translate(50.589 0)" fill="#4a4a4a"/>
<rect id="Rectangle_61" data-name="Rectangle 61" width="5.336" height="5.336" rx="1" transform="translate(56.912 0)" fill="#4a4a4a"/>
<rect id="Rectangle_62" data-name="Rectangle 62" width="5.336" height="5.336" rx="1" transform="translate(63.236 0)" fill="#4a4a4a"/>
</g>
<path id="Path_55" data-name="Path 55" d="M1.094,0H8A1.093,1.093,0,0,1,9.091,1.093v3.15A1.093,1.093,0,0,1,8,5.336H1.093A1.093,1.093,0,0,1,0,4.243V1.094A1.093,1.093,0,0,1,1.093,0Z" transform="translate(80.428 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_7" data-name="Group 7" transform="translate(1.531 29.627)">
<rect id="Rectangle_63" data-name="Rectangle 63" width="5.336" height="5.336" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
<rect id="Rectangle_64" data-name="Rectangle 64" width="5.336" height="5.336" rx="1" transform="translate(6.324 0)" fill="#4a4a4a"/>
<rect id="Rectangle_65" data-name="Rectangle 65" width="5.336" height="5.336" rx="1" transform="translate(12.647 0)" fill="#4a4a4a"/>
<rect id="Rectangle_66" data-name="Rectangle 66" width="5.336" height="5.336" rx="1" transform="translate(18.971 0)" fill="#4a4a4a"/>
<path id="Path_56" data-name="Path 56" d="M1.093,0H31.515a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H1.093A1.093,1.093,0,0,1,0,4.244V1.093A1.093,1.093,0,0,1,1.093,0ZM34.687,0h3.942a1.093,1.093,0,0,1,1.093,1.093V4.244a1.093,1.093,0,0,1-1.093,1.093H34.687a1.093,1.093,0,0,1-1.093-1.093V1.093A1.093,1.093,0,0,1,34.687,0Z" transform="translate(25.294 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_67" data-name="Rectangle 67" width="5.336" height="5.336" rx="1" transform="translate(66.003 0)" fill="#4a4a4a"/>
<rect id="Rectangle_68" data-name="Rectangle 68" width="5.336" height="5.336" rx="1" transform="translate(72.327 0)" fill="#4a4a4a"/>
<rect id="Rectangle_69" data-name="Rectangle 69" width="5.336" height="5.336" rx="1" transform="translate(84.183 0)" fill="#4a4a4a"/>
<path id="Path_57" data-name="Path 57" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(83.59 2.273) rotate(180)" fill="#4a4a4a"/>
<path id="Path_58" data-name="Path 58" d="M5.336,0V1.18A1.093,1.093,0,0,1,4.243,2.273H1.093A1.093,1.093,0,0,1,0,1.18V0Z" transform="translate(78.255 3.063)" fill="#4a4a4a"/>
</g>
<rect id="Rectangle_70" data-name="Rectangle 70" width="88.927" height="2.371" rx="1.085" transform="translate(1.925 1.17)" fill="#4a4a4a"/>
<rect id="Rectangle_71" data-name="Rectangle 71" width="4.986" height="1.581" rx="0.723" transform="translate(4.1 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_72" data-name="Rectangle 72" width="4.986" height="1.581" rx="0.723" transform="translate(10.923 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_73" data-name="Rectangle 73" width="4.986" height="1.581" rx="0.723" transform="translate(16.173 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_74" data-name="Rectangle 74" width="4.986" height="1.581" rx="0.723" transform="translate(21.421 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_75" data-name="Rectangle 75" width="4.986" height="1.581" rx="0.723" transform="translate(26.671 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_76" data-name="Rectangle 76" width="4.986" height="1.581" rx="0.723" transform="translate(33.232 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_77" data-name="Rectangle 77" width="4.986" height="1.581" rx="0.723" transform="translate(38.48 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_78" data-name="Rectangle 78" width="4.986" height="1.581" rx="0.723" transform="translate(43.73 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_79" data-name="Rectangle 79" width="4.986" height="1.581" rx="0.723" transform="translate(48.978 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_80" data-name="Rectangle 80" width="4.986" height="1.581" rx="0.723" transform="translate(55.54 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_81" data-name="Rectangle 81" width="4.986" height="1.581" rx="0.723" transform="translate(60.788 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_82" data-name="Rectangle 82" width="4.986" height="1.581" rx="0.723" transform="translate(66.038 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_83" data-name="Rectangle 83" width="4.986" height="1.581" rx="0.723" transform="translate(72.599 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_84" data-name="Rectangle 84" width="4.986" height="1.581" rx="0.723" transform="translate(77.847 1.566)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_85" data-name="Rectangle 85" width="4.986" height="1.581" rx="0.723" transform="translate(83.097 1.566)" fill="#d8d8d8" opacity="0.136"/>
</g>
<path id="Path_59" data-name="Path 59" d="M146.71,159.855a5.439,5.439,0,0,0-.7.07c-.042-.164-.081-.329-.127-.493a5.457,5.457,0,1,0-5.4-9.372q-.181-.185-.366-.367a5.454,5.454,0,1,0-9.384-5.4c-.162-.046-.325-.084-.486-.126a5.467,5.467,0,1,0-10.788,0c-.162.042-.325.08-.486.126a5.457,5.457,0,1,0-9.384,5.4,21.843,21.843,0,1,0,36.421,21.02,5.452,5.452,0,1,0,.7-10.858" transform="translate(6.275 -6.025)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_60" data-name="Path 60" d="M83,124.855h43.71V103H83Z" transform="translate(4.42 -9.271)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_61" data-name="Path 61" d="M134.855,116.765a2.732,2.732,0,1,0,0-5.464,2.811,2.811,0,0,0-.349.035c-.022-.082-.04-.164-.063-.246a2.733,2.733,0,0,0-1.052-5.253,2.7,2.7,0,0,0-1.648.566q-.09-.093-.184-.184a2.7,2.7,0,0,0,.553-1.633,2.732,2.732,0,0,0-5.245-1.07,10.928,10.928,0,1,0,0,21.031,2.732,2.732,0,0,0,5.245-1.07,2.7,2.7,0,0,0-.553-1.633q.093-.09.184-.184a2.7,2.7,0,0,0,1.648.566,2.732,2.732,0,0,0,1.052-5.253c.023-.081.042-.164.063-.246a2.811,2.811,0,0,0,.349.035" transform="translate(7.202 -9.377)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_62" data-name="Path 62" d="M143.232,42.33a2.967,2.967,0,0,1-.535-.055,2.754,2.754,0,0,1-.514-.153,2.838,2.838,0,0,1-.471-.251,4.139,4.139,0,0,1-.415-.339,3.2,3.2,0,0,1-.338-.415A2.7,2.7,0,0,1,140.5,39.6a2.968,2.968,0,0,1,.055-.535,3.152,3.152,0,0,1,.152-.514,2.874,2.874,0,0,1,.252-.47,2.633,2.633,0,0,1,.753-.754,2.837,2.837,0,0,1,.471-.251,2.753,2.753,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,4.019,4.019,0,0,1,.339.415,2.786,2.786,0,0,1,.251.47,2.864,2.864,0,0,1,.208,1.049,2.77,2.77,0,0,1-.8,1.934,4.139,4.139,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459m21.855-1.366a2.789,2.789,0,0,1-1.935-.8,4.162,4.162,0,0,1-.338-.415,2.7,2.7,0,0,1-.459-1.519,2.789,2.789,0,0,1,.8-1.934,4.139,4.139,0,0,1,.415-.339,2.838,2.838,0,0,1,.471-.251,2.752,2.752,0,0,1,.514-.153,2.527,2.527,0,0,1,1.071,0,2.654,2.654,0,0,1,.983.4,4.139,4.139,0,0,1,.415.339,2.79,2.79,0,0,1,.8,1.934,3.069,3.069,0,0,1-.055.535,2.779,2.779,0,0,1-.153.514,3.885,3.885,0,0,1-.251.47,4.02,4.02,0,0,1-.339.415,4.138,4.138,0,0,1-.415.339,2.722,2.722,0,0,1-1.519.459" transform="translate(9.753 -15.532)" fill-rule="evenodd"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,170 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1041.277" height="554.141" viewBox="0 0 1041.277 554.141">
<title>Powered by React</title>
<g id="Group_24" data-name="Group 24" transform="translate(-440 -263)">
<g id="Group_23" data-name="Group 23" transform="translate(439.989 262.965)">
<path id="Path_299" data-name="Path 299" d="M1040.82,611.12q-1.74,3.75-3.47,7.4-2.7,5.67-5.33,11.12c-.78,1.61-1.56,3.19-2.32,4.77-8.6,17.57-16.63,33.11-23.45,45.89A73.21,73.21,0,0,1,942.44,719l-151.65,1.65h-1.6l-13,.14-11.12.12-34.1.37h-1.38l-17.36.19h-.53l-107,1.16-95.51,1-11.11.12-69,.75H429l-44.75.48h-.48l-141.5,1.53-42.33.46a87.991,87.991,0,0,1-10.79-.54h0c-1.22-.14-2.44-.3-3.65-.49a87.38,87.38,0,0,1-51.29-27.54C116,678.37,102.75,655,93.85,629.64q-1.93-5.49-3.6-11.12C59.44,514.37,97,380,164.6,290.08q4.25-5.64,8.64-11l.07-.08c20.79-25.52,44.1-46.84,68.93-62,44-26.91,92.75-34.49,140.7-11.9,40.57,19.12,78.45,28.11,115.17,30.55,3.71.24,7.42.42,11.11.53,84.23,2.65,163.17-27.7,255.87-47.29,3.69-.78,7.39-1.55,11.12-2.28,66.13-13.16,139.49-20.1,226.73-5.51a189.089,189.089,0,0,1,26.76,6.4q5.77,1.86,11.12,4c41.64,16.94,64.35,48.24,74,87.46q1.37,5.46,2.37,11.11C1134.3,384.41,1084.19,518.23,1040.82,611.12Z" transform="translate(-79.34 -172.91)" fill="#f2f2f2"/>
<path id="Path_300" data-name="Path 300" d="M576.36,618.52a95.21,95.21,0,0,1-1.87,11.12h93.7V618.52Zm-78.25,62.81,11.11-.09V653.77c-3.81-.17-7.52-.34-11.11-.52ZM265.19,618.52v11.12h198.5V618.52ZM1114.87,279h-74V191.51q-5.35-2.17-11.12-4V279H776.21V186.58c-3.73.73-7.43,1.5-11.12,2.28V279H509.22V236.15c-3.69-.11-7.4-.29-11.11-.53V279H242.24V217c-24.83,15.16-48.14,36.48-68.93,62h-.07v.08q-4.4,5.4-8.64,11h8.64V618.52h-83q1.66,5.63,3.6,11.12h79.39v93.62a87,87,0,0,0,12.2,2.79c1.21.19,2.43.35,3.65.49h0a87.991,87.991,0,0,0,10.79.54l42.33-.46v-97H498.11v94.21l11.11-.12V629.64H765.09V721l11.12-.12V629.64H1029.7v4.77c.76-1.58,1.54-3.16,2.32-4.77q2.63-5.45,5.33-11.12,1.73-3.64,3.47-7.4v-321h76.42Q1116.23,284.43,1114.87,279ZM242.24,618.52V290.08H498.11V618.52Zm267,0V290.08H765.09V618.52Zm520.48,0H776.21V290.08H1029.7Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_301" data-name="Path 301" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" fill="#65617d"/>
<path id="Path_302" data-name="Path 302" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l46.65-28,93.6-.78,2-.01.66-.01,2-.03,44.94-.37,2.01-.01.64-.01,2-.01L315,509.3l.38-.01,35.55-.3h.29l277.4-2.34,6.79-.05h.68l5.18-.05,37.65-.31,2-.03,1.85-.02h.96l11.71-.09,2.32-.03,3.11-.02,9.75-.09,15.47-.13,2-.02,3.48-.02h.65l74.71-.64Z" opacity="0.2"/>
<path id="Path_303" data-name="Path 303" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
<path id="Path_304" data-name="Path 304" d="M375.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_305" data-name="Path 305" d="M377.44,656.57v24.49a6.13,6.13,0,0,1-3.5,5.54,6,6,0,0,1-2.5.6l-34.9.74a6,6,0,0,1-2.7-.57,6.12,6.12,0,0,1-3.57-5.57V656.57Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
<rect id="Rectangle_137" data-name="Rectangle 137" width="47.17" height="31.5" transform="translate(680.92 483.65)" fill="#3f3d56"/>
<rect id="Rectangle_138" data-name="Rectangle 138" width="47.17" height="31.5" transform="translate(680.92 483.65)" opacity="0.1"/>
<rect id="Rectangle_139" data-name="Rectangle 139" width="47.17" height="31.5" transform="translate(678.92 483.65)" fill="#3f3d56"/>
<path id="Path_306" data-name="Path 306" d="M298.09,483.65v4.97l-47.17,1.26v-6.23Z" opacity="0.1"/>
<path id="Path_307" data-name="Path 307" d="M460.69,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6a4,4,0,0,1,3.95,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
<path id="Path_308" data-name="Path 308" d="M265.19,481.32v181.2h-.05a4,4,0,0,1-3.95-3.95V485.27a4,4,0,0,1,3.95-3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_309" data-name="Path 309" d="M194.59,319.15h177.5V467.4l-177.5,4Z" fill="#39374d"/>
<path id="Path_310" data-name="Path 310" d="M726.09,483.65v6.41l-47.17-1.26v-5.15Z" opacity="0.1"/>
<path id="Path_311" data-name="Path 311" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0L672,657.42a4,4,0,0,1-3.85-3.95V485.27a4,4,0,0,1,3.95-3.95H863.7a4,4,0,0,1,3.99,3.95Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
<path id="Path_312" data-name="Path 312" d="M867.69,485.27v173.3a4,4,0,0,1-4,3.95h0V481.32h0a4,4,0,0,1,4,3.95Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_313" data-name="Path 313" d="M775.59,319.15H598.09V467.4l177.5,4Z" fill="#39374d"/>
<path id="Path_314" data-name="Path 314" d="M663.19,485.27v168.2a4,4,0,0,1-3.85,3.95l-191.65,5.1h0a4,4,0,0,1-4-3.95V485.27a4,4,0,0,1,3.95-3.95h191.6A4,4,0,0,1,663.19,485.27Z" transform="translate(-79.34 -172.91)" fill="#65617d"/>
<path id="Path_315" data-name="Path 315" d="M397.09,319.15h177.5V467.4l-177.5,4Z" fill="#4267b2"/>
<path id="Path_316" data-name="Path 316" d="M863.09,533.65v13l-151.92,1.4-1.62.03-57.74.53-1.38.02-17.55.15h-.52l-106.98.99L349.77,551.4h-.15l-44.65.42-.48.01-198.4,1.82v-15l202.51-1.33h.48l40.99-.28h.19l283.08-1.87h.29l.17-.01h.47l4.79-.03h1.46l74.49-.5,4.4-.02.98-.01Z" opacity="0.1"/>
<circle id="Ellipse_111" data-name="Ellipse 111" cx="51.33" cy="51.33" r="51.33" transform="translate(435.93 246.82)" fill="#fbbebe"/>
<path id="Path_317" data-name="Path 317" d="M617.94,550.07s-99.5,12-90,0c3.44-4.34,4.39-17.2,4.2-31.85-.06-4.45-.22-9.06-.45-13.65-1.1-22-3.75-43.5-3.75-43.5s87-41,77-8.5c-4,13.13-2.69,31.57.35,48.88.89,5.05,1.92,10,3,14.7a344.66,344.66,0,0,0,9.65,33.92Z" transform="translate(-79.34 -172.91)" fill="#fbbebe"/>
<path id="Path_318" data-name="Path 318" d="M585.47,546c11.51-2.13,23.7-6,34.53-1.54,2.85,1.17,5.47,2.88,8.39,3.86s6.12,1.22,9.16,1.91c10.68,2.42,19.34,10.55,24.9,20s8.44,20.14,11.26,30.72l6.9,25.83c6,22.45,12,45.09,13.39,68.3a2437.506,2437.506,0,0,1-250.84,1.43c5.44-10.34,11-21.31,10.54-33s-7.19-23.22-4.76-34.74c1.55-7.34,6.57-13.39,9.64-20.22,8.75-19.52,1.94-45.79,17.32-60.65,6.92-6.68,17-9.21,26.63-8.89,12.28.41,24.85,4.24,37,6.11C555.09,547.48,569.79,548.88,585.47,546Z" transform="translate(-79.34 -172.91)" fill="#ff6584"/>
<path id="Path_319" data-name="Path 319" d="M716.37,657.17l-.1,1.43v.1l-.17,2.3-1.33,18.51-1.61,22.3-.46,6.28-1,13.44v.17l-107,1-175.59,1.9v.84h-.14v-1.12l.45-14.36.86-28.06.74-23.79.07-2.37a10.53,10.53,0,0,1,11.42-10.17c4.72.4,10.85.89,18.18,1.41l3,.22c42.33,2.94,120.56,6.74,199.5,2,1.66-.09,3.33-.19,5-.31,12.24-.77,24.47-1.76,36.58-3a10.53,10.53,0,0,1,11.6,11.23Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_320" data-name="Path 320" d="M429.08,725.44v-.84l175.62-1.91,107-1h.3v-.17l1-13.44.43-6,1.64-22.61,1.29-17.9v-.44a10.617,10.617,0,0,0-.11-2.47.3.3,0,0,0,0-.1,10.391,10.391,0,0,0-2-4.64,10.54,10.54,0,0,0-9.42-4c-12.11,1.24-24.34,2.23-36.58,3-1.67.12-3.34.22-5,.31-78.94,4.69-157.17.89-199.5-2l-3-.22c-7.33-.52-13.46-1-18.18-1.41a10.54,10.54,0,0,0-11.24,8.53,11,11,0,0,0-.18,1.64l-.68,22.16L429.54,710l-.44,14.36v1.12Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
<path id="Path_321" data-name="Path 321" d="M716.67,664.18l-1.23,15.33-1.83,22.85-.46,5.72-1,12.81-.06.64v.17h0l-.15,1.48.11-1.48h-.29l-107,1-175.65,1.9v-.28l.49-14.36,1-28.06.64-18.65A6.36,6.36,0,0,1,434.3,658a6.25,6.25,0,0,1,3.78-.9c2.1.17,4.68.37,7.69.59,4.89.36,10.92.78,17.94,1.22,13,.82,29.31,1.7,48,2.42,52,2,122.2,2.67,188.88-3.17,3-.26,6.1-.55,9.13-.84a6.26,6.26,0,0,1,3.48.66,5.159,5.159,0,0,1,.86.54,6.14,6.14,0,0,1,2,2.46,3.564,3.564,0,0,1,.25.61A6.279,6.279,0,0,1,716.67,664.18Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_322" data-name="Path 322" d="M377.44,677.87v3.19a6.13,6.13,0,0,1-3.5,5.54l-40.1.77a6.12,6.12,0,0,1-3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_323" data-name="Path 323" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
<path id="Path_324" data-name="Path 324" d="M298.59,515.57l-52.25,1V507.9l52.25-1Z" opacity="0.1"/>
<path id="Path_325" data-name="Path 325" d="M300.59,515.57l-52.25,1V507.9l52.25-1Z" fill="#3f3d56"/>
<path id="Path_326" data-name="Path 326" d="M758.56,679.87v3.19a6.13,6.13,0,0,0,3.5,5.54l40.1.77a6.12,6.12,0,0,0,3.57-5.57v-3Z" transform="translate(-79.34 -172.91)" opacity="0.1"/>
<path id="Path_327" data-name="Path 327" d="M678.72,517.57l52.25,1V509.9l-52.25-1Z" opacity="0.1"/>
<path id="Path_328" data-name="Path 328" d="M676.72,517.57l52.25,1V509.9l-52.25-1Z" fill="#3f3d56"/>
<path id="Path_329" data-name="Path 329" d="M534.13,486.79c.08,7-3.16,13.6-5.91,20.07a163.491,163.491,0,0,0-12.66,74.71c.73,11,2.58,22,.73,32.9s-8.43,21.77-19,24.9c17.53,10.45,41.26,9.35,57.76-2.66,8.79-6.4,15.34-15.33,21.75-24.11a97.86,97.86,0,0,1-13.31,44.75A103.43,103.43,0,0,0,637,616.53c4.31-5.81,8.06-12.19,9.72-19.23,3.09-13-1.22-26.51-4.51-39.5a266.055,266.055,0,0,1-6.17-33c-.43-3.56-.78-7.22.1-10.7,1-4.07,3.67-7.51,5.64-11.22,5.6-10.54,5.73-23.3,2.86-34.88s-8.49-22.26-14.06-32.81c-4.46-8.46-9.3-17.31-17.46-22.28-5.1-3.1-11-4.39-16.88-5.64l-25.37-5.43c-5.55-1.19-11.26-2.38-16.87-1.51-9.47,1.48-16.14,8.32-22,15.34-4.59,5.46-15.81,15.71-16.6,22.86-.72,6.59,5.1,17.63,6.09,24.58,1.3,9,2.22,6,7.3,11.52C532,478.05,534.07,482,534.13,486.79Z" transform="translate(-79.34 -172.91)" fill="#3f3d56"/>
</g>
<g id="docusaurus_keytar" transform="translate(670.271 615.768)">
<path id="Path_40" data-name="Path 40" d="M99,52h43.635V69.662H99Z" transform="translate(-49.132 -33.936)" fill="#fff" fill-rule="evenodd"/>
<path id="Path_41" data-name="Path 41" d="M13.389,158.195A10.377,10.377,0,0,1,4.4,153a10.377,10.377,0,0,0,8.988,15.584H23.779V158.195Z" transform="translate(-3 -82.47)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_42" data-name="Path 42" d="M66.967,38.083l36.373-2.273V30.615A10.389,10.389,0,0,0,92.95,20.226H46.2l-1.3-2.249a1.5,1.5,0,0,0-2.6,0L41,20.226l-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-1.3-2.249a1.5,1.5,0,0,0-2.6,0l-1.3,2.249-.034,0-2.152-2.151a1.5,1.5,0,0,0-2.508.672L25.21,21.4l-2.7-.723a1.5,1.5,0,0,0-1.836,1.837l.722,2.7-2.65.71a1.5,1.5,0,0,0-.673,2.509l2.152,2.152c0,.011,0,.022,0,.033l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6L20.226,41l-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3-2.249,1.3a1.5,1.5,0,0,0,0,2.6l2.249,1.3A10.389,10.389,0,0,0,30.615,103.34H92.95A10.389,10.389,0,0,0,103.34,92.95V51.393L66.967,49.12a5.53,5.53,0,0,1,0-11.038" transform="translate(-9.836 -17.226)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_43" data-name="Path 43" d="M143,163.779h15.584V143H143Z" transform="translate(-70.275 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_44" data-name="Path 44" d="M173.779,148.389a2.582,2.582,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-75.08 -75.262)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_45" data-name="Path 45" d="M153,113.389h15.584V103H153Z" transform="translate(-75.08 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_46" data-name="Path 46" d="M183.389,108.944a1.3,1.3,0,1,0,0-2.6,1.336,1.336,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.337,1.337,0,0,0,.166.017" transform="translate(-84.691 -57.894)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_47" data-name="Path 47" d="M52.188,48.292a1.3,1.3,0,0,1-1.3-1.3,3.9,3.9,0,0,0-7.792,0,1.3,1.3,0,1,1-2.6,0,6.493,6.493,0,0,1,12.987,0,1.3,1.3,0,0,1-1.3,1.3" transform="translate(-21.02 -28.41)" fill-rule="evenodd"/>
<path id="Path_48" data-name="Path 48" d="M103,139.752h31.168a10.389,10.389,0,0,0,10.389-10.389V93H113.389A10.389,10.389,0,0,0,103,103.389Z" transform="translate(-51.054 -53.638)" fill="#ffff50" fill-rule="evenodd"/>
<path id="Path_49" data-name="Path 49" d="M141.1,94.017H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0-25.877H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.293H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m0,10.389H115.106a.519.519,0,1,1,0-1.039H141.1a.519.519,0,0,1,0,1.039m7.782-47.993c-.006,0-.011,0-.018,0-1.605.055-2.365,1.66-3.035,3.077-.7,1.48-1.24,2.443-2.126,2.414-.981-.035-1.542-1.144-2.137-2.317-.683-1.347-1.462-2.876-3.1-2.819-1.582.054-2.344,1.451-3.017,2.684-.715,1.313-1.2,2.112-2.141,2.075-1-.036-1.533-.938-2.149-1.981-.686-1.162-1.479-2.467-3.084-2.423-1.555.053-2.319,1.239-2.994,2.286-.713,1.106-1.213,1.781-2.164,1.741-1.025-.036-1.554-.784-2.167-1.65-.688-.973-1.463-2.074-3.062-2.021a3.815,3.815,0,0,0-2.959,1.879c-.64.812-1.14,1.456-2.2,1.415a.52.52,0,0,0-.037,1.039,3.588,3.588,0,0,0,3.05-1.811c.611-.777,1.139-1.448,2.178-1.483,1-.043,1.47.579,2.179,1.582.674.953,1.438,2.033,2.977,2.089,1.612.054,2.387-1.151,3.074-2.217.614-.953,1.144-1.775,2.156-1.81.931-.035,1.438.7,2.153,1.912.674,1.141,1.437,2.434,3.006,2.491,1.623.056,2.407-1.361,3.09-2.616.592-1.085,1.15-2.109,2.14-2.143.931-.022,1.417.829,2.135,2.249.671,1.326,1.432,2.828,3.026,2.886l.088,0c1.592,0,2.347-1.6,3.015-3.01.592-1.252,1.152-2.431,2.113-2.479Z" transform="translate(-55.378 -38.552)" fill-rule="evenodd"/>
<path id="Path_50" data-name="Path 50" d="M83,163.779h20.779V143H83Z" transform="translate(-41.443 -77.665)" fill="#3ecc5f" fill-rule="evenodd"/>
<g id="Group_8" data-name="Group 8" transform="matrix(0.966, -0.259, 0.259, 0.966, 51.971, 43.3)">
<rect id="Rectangle_3" data-name="Rectangle 3" width="43.906" height="17.333" rx="2" transform="translate(0 0)" fill="#d8d8d8"/>
<g id="Group_2" data-name="Group 2" transform="translate(0.728 10.948)">
<rect id="Rectangle_4" data-name="Rectangle 4" width="2.537" height="2.537" rx="1" transform="translate(7.985 0)" fill="#4a4a4a"/>
<rect id="Rectangle_5" data-name="Rectangle 5" width="2.537" height="2.537" rx="1" transform="translate(10.991 0)" fill="#4a4a4a"/>
<rect id="Rectangle_6" data-name="Rectangle 6" width="2.537" height="2.537" rx="1" transform="translate(13.997 0)" fill="#4a4a4a"/>
<rect id="Rectangle_7" data-name="Rectangle 7" width="2.537" height="2.537" rx="1" transform="translate(17.003 0)" fill="#4a4a4a"/>
<rect id="Rectangle_8" data-name="Rectangle 8" width="2.537" height="2.537" rx="1" transform="translate(20.009 0)" fill="#4a4a4a"/>
<rect id="Rectangle_9" data-name="Rectangle 9" width="2.537" height="2.537" rx="1" transform="translate(23.015 0)" fill="#4a4a4a"/>
<rect id="Rectangle_10" data-name="Rectangle 10" width="2.537" height="2.537" rx="1" transform="translate(26.021 0)" fill="#4a4a4a"/>
<rect id="Rectangle_11" data-name="Rectangle 11" width="2.537" height="2.537" rx="1" transform="translate(29.028 0)" fill="#4a4a4a"/>
<rect id="Rectangle_12" data-name="Rectangle 12" width="2.537" height="2.537" rx="1" transform="translate(32.034 0)" fill="#4a4a4a"/>
<path id="Path_51" data-name="Path 51" d="M.519,0H6.9A.519.519,0,0,1,7.421.52v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0ZM35.653,0h6.383a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H35.652a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,35.652,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_3" data-name="Group 3" transform="translate(0.728 4.878)">
<path id="Path_52" data-name="Path 52" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_13" data-name="Rectangle 13" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
<rect id="Rectangle_14" data-name="Rectangle 14" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
<rect id="Rectangle_15" data-name="Rectangle 15" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
<rect id="Rectangle_16" data-name="Rectangle 16" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
<rect id="Rectangle_17" data-name="Rectangle 17" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
<rect id="Rectangle_18" data-name="Rectangle 18" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
<rect id="Rectangle_19" data-name="Rectangle 19" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
<rect id="Rectangle_20" data-name="Rectangle 20" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
<rect id="Rectangle_21" data-name="Rectangle 21" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
<rect id="Rectangle_22" data-name="Rectangle 22" width="2.537" height="2.537" rx="1" transform="translate(31 0)" fill="#4a4a4a"/>
<rect id="Rectangle_23" data-name="Rectangle 23" width="2.537" height="2.537" rx="1" transform="translate(34.006 0)" fill="#4a4a4a"/>
<rect id="Rectangle_24" data-name="Rectangle 24" width="2.537" height="2.537" rx="1" transform="translate(37.012 0)" fill="#4a4a4a"/>
<rect id="Rectangle_25" data-name="Rectangle 25" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
</g>
<g id="Group_4" data-name="Group 4" transform="translate(43.283 4.538) rotate(180)">
<path id="Path_53" data-name="Path 53" d="M.519,0H2.956a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.519A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_26" data-name="Rectangle 26" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
<rect id="Rectangle_27" data-name="Rectangle 27" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
<rect id="Rectangle_28" data-name="Rectangle 28" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
<rect id="Rectangle_29" data-name="Rectangle 29" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
<rect id="Rectangle_30" data-name="Rectangle 30" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
<rect id="Rectangle_31" data-name="Rectangle 31" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
<rect id="Rectangle_32" data-name="Rectangle 32" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
<rect id="Rectangle_33" data-name="Rectangle 33" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
<rect id="Rectangle_34" data-name="Rectangle 34" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
<rect id="Rectangle_35" data-name="Rectangle 35" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
<rect id="Rectangle_36" data-name="Rectangle 36" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
<rect id="Rectangle_37" data-name="Rectangle 37" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
<rect id="Rectangle_38" data-name="Rectangle 38" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
<rect id="Rectangle_39" data-name="Rectangle 39" width="2.537" height="2.537" rx="1" transform="translate(3.945 0)" fill="#4a4a4a"/>
<rect id="Rectangle_40" data-name="Rectangle 40" width="2.537" height="2.537" rx="1" transform="translate(6.951 0)" fill="#4a4a4a"/>
<rect id="Rectangle_41" data-name="Rectangle 41" width="2.537" height="2.537" rx="1" transform="translate(9.958 0)" fill="#4a4a4a"/>
<rect id="Rectangle_42" data-name="Rectangle 42" width="2.537" height="2.537" rx="1" transform="translate(12.964 0)" fill="#4a4a4a"/>
<rect id="Rectangle_43" data-name="Rectangle 43" width="2.537" height="2.537" rx="1" transform="translate(15.97 0)" fill="#4a4a4a"/>
<rect id="Rectangle_44" data-name="Rectangle 44" width="2.537" height="2.537" rx="1" transform="translate(18.976 0)" fill="#4a4a4a"/>
<rect id="Rectangle_45" data-name="Rectangle 45" width="2.537" height="2.537" rx="1" transform="translate(21.982 0)" fill="#4a4a4a"/>
<rect id="Rectangle_46" data-name="Rectangle 46" width="2.537" height="2.537" rx="1" transform="translate(24.988 0)" fill="#4a4a4a"/>
<rect id="Rectangle_47" data-name="Rectangle 47" width="2.537" height="2.537" rx="1" transform="translate(27.994 0)" fill="#4a4a4a"/>
<rect id="Rectangle_48" data-name="Rectangle 48" width="2.537" height="2.537" rx="1" transform="translate(31.001 0)" fill="#4a4a4a"/>
<rect id="Rectangle_49" data-name="Rectangle 49" width="2.537" height="2.537" rx="1" transform="translate(34.007 0)" fill="#4a4a4a"/>
<rect id="Rectangle_50" data-name="Rectangle 50" width="2.537" height="2.537" rx="1" transform="translate(37.013 0)" fill="#4a4a4a"/>
<rect id="Rectangle_51" data-name="Rectangle 51" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
</g>
<g id="Group_6" data-name="Group 6" transform="translate(0.728 7.883)">
<path id="Path_54" data-name="Path 54" d="M.519,0h3.47a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(0 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<g id="Group_5" data-name="Group 5" transform="translate(5.073 0)">
<rect id="Rectangle_52" data-name="Rectangle 52" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
<rect id="Rectangle_53" data-name="Rectangle 53" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
<rect id="Rectangle_54" data-name="Rectangle 54" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
<rect id="Rectangle_55" data-name="Rectangle 55" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
<rect id="Rectangle_56" data-name="Rectangle 56" width="2.537" height="2.537" rx="1" transform="translate(12.025 0)" fill="#4a4a4a"/>
<rect id="Rectangle_57" data-name="Rectangle 57" width="2.537" height="2.537" rx="1" transform="translate(15.031 0)" fill="#4a4a4a"/>
<rect id="Rectangle_58" data-name="Rectangle 58" width="2.537" height="2.537" rx="1" transform="translate(18.037 0)" fill="#4a4a4a"/>
<rect id="Rectangle_59" data-name="Rectangle 59" width="2.537" height="2.537" rx="1" transform="translate(21.042 0)" fill="#4a4a4a"/>
<rect id="Rectangle_60" data-name="Rectangle 60" width="2.537" height="2.537" rx="1" transform="translate(24.049 0)" fill="#4a4a4a"/>
<rect id="Rectangle_61" data-name="Rectangle 61" width="2.537" height="2.537" rx="1" transform="translate(27.055 0)" fill="#4a4a4a"/>
<rect id="Rectangle_62" data-name="Rectangle 62" width="2.537" height="2.537" rx="1" transform="translate(30.061 0)" fill="#4a4a4a"/>
</g>
<path id="Path_55" data-name="Path 55" d="M.52,0H3.8a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.017V.52A.519.519,0,0,1,.519,0Z" transform="translate(38.234 0)" fill="#4a4a4a" fill-rule="evenodd"/>
</g>
<g id="Group_7" data-name="Group 7" transform="translate(0.728 14.084)">
<rect id="Rectangle_63" data-name="Rectangle 63" width="2.537" height="2.537" rx="1" transform="translate(0 0)" fill="#4a4a4a"/>
<rect id="Rectangle_64" data-name="Rectangle 64" width="2.537" height="2.537" rx="1" transform="translate(3.006 0)" fill="#4a4a4a"/>
<rect id="Rectangle_65" data-name="Rectangle 65" width="2.537" height="2.537" rx="1" transform="translate(6.012 0)" fill="#4a4a4a"/>
<rect id="Rectangle_66" data-name="Rectangle 66" width="2.537" height="2.537" rx="1" transform="translate(9.018 0)" fill="#4a4a4a"/>
<path id="Path_56" data-name="Path 56" d="M.519,0H14.981A.519.519,0,0,1,15.5.519v1.5a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,2.018V.519A.519.519,0,0,1,.519,0Zm15.97,0h1.874a.519.519,0,0,1,.519.519v1.5a.519.519,0,0,1-.519.519H16.489a.519.519,0,0,1-.519-.519V.519A.519.519,0,0,1,16.489,0Z" transform="translate(12.024 0)" fill="#4a4a4a" fill-rule="evenodd"/>
<rect id="Rectangle_67" data-name="Rectangle 67" width="2.537" height="2.537" rx="1" transform="translate(31.376 0)" fill="#4a4a4a"/>
<rect id="Rectangle_68" data-name="Rectangle 68" width="2.537" height="2.537" rx="1" transform="translate(34.382 0)" fill="#4a4a4a"/>
<rect id="Rectangle_69" data-name="Rectangle 69" width="2.537" height="2.537" rx="1" transform="translate(40.018 0)" fill="#4a4a4a"/>
<path id="Path_57" data-name="Path 57" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(39.736 1.08) rotate(180)" fill="#4a4a4a"/>
<path id="Path_58" data-name="Path 58" d="M2.537,0V.561a.519.519,0,0,1-.519.519H.519A.519.519,0,0,1,0,.561V0Z" transform="translate(37.2 1.456)" fill="#4a4a4a"/>
</g>
<rect id="Rectangle_70" data-name="Rectangle 70" width="42.273" height="1.127" rx="0.564" transform="translate(0.915 0.556)" fill="#4a4a4a"/>
<rect id="Rectangle_71" data-name="Rectangle 71" width="2.37" height="0.752" rx="0.376" transform="translate(1.949 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_72" data-name="Rectangle 72" width="2.37" height="0.752" rx="0.376" transform="translate(5.193 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_73" data-name="Rectangle 73" width="2.37" height="0.752" rx="0.376" transform="translate(7.688 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_74" data-name="Rectangle 74" width="2.37" height="0.752" rx="0.376" transform="translate(10.183 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_75" data-name="Rectangle 75" width="2.37" height="0.752" rx="0.376" transform="translate(12.679 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_76" data-name="Rectangle 76" width="2.37" height="0.752" rx="0.376" transform="translate(15.797 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_77" data-name="Rectangle 77" width="2.37" height="0.752" rx="0.376" transform="translate(18.292 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_78" data-name="Rectangle 78" width="2.37" height="0.752" rx="0.376" transform="translate(20.788 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_79" data-name="Rectangle 79" width="2.37" height="0.752" rx="0.376" transform="translate(23.283 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_80" data-name="Rectangle 80" width="2.37" height="0.752" rx="0.376" transform="translate(26.402 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_81" data-name="Rectangle 81" width="2.37" height="0.752" rx="0.376" transform="translate(28.897 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_82" data-name="Rectangle 82" width="2.37" height="0.752" rx="0.376" transform="translate(31.393 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_83" data-name="Rectangle 83" width="2.37" height="0.752" rx="0.376" transform="translate(34.512 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_84" data-name="Rectangle 84" width="2.37" height="0.752" rx="0.376" transform="translate(37.007 0.744)" fill="#d8d8d8" opacity="0.136"/>
<rect id="Rectangle_85" data-name="Rectangle 85" width="2.37" height="0.752" rx="0.376" transform="translate(39.502 0.744)" fill="#d8d8d8" opacity="0.136"/>
</g>
<path id="Path_59" data-name="Path 59" d="M123.779,148.389a2.583,2.583,0,0,0-.332.033c-.02-.078-.038-.156-.06-.234a2.594,2.594,0,1,0-2.567-4.455q-.086-.088-.174-.175a2.593,2.593,0,1,0-4.461-2.569c-.077-.022-.154-.04-.231-.06a2.6,2.6,0,1,0-5.128,0c-.077.02-.154.038-.231.06a2.594,2.594,0,1,0-4.461,2.569,10.384,10.384,0,1,0,17.314,9.992,2.592,2.592,0,1,0,.332-5.161" transform="translate(-51.054 -75.262)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_60" data-name="Path 60" d="M83,113.389h20.779V103H83Z" transform="translate(-41.443 -58.444)" fill="#3ecc5f" fill-rule="evenodd"/>
<path id="Path_61" data-name="Path 61" d="M123.389,108.944a1.3,1.3,0,1,0,0-2.6,1.338,1.338,0,0,0-.166.017c-.01-.039-.019-.078-.03-.117a1.3,1.3,0,0,0-.5-2.5,1.285,1.285,0,0,0-.783.269q-.043-.044-.087-.087a1.285,1.285,0,0,0,.263-.776,1.3,1.3,0,0,0-2.493-.509,5.195,5.195,0,1,0,0,10,1.3,1.3,0,0,0,2.493-.509,1.285,1.285,0,0,0-.263-.776q.044-.043.087-.087a1.285,1.285,0,0,0,.783.269,1.3,1.3,0,0,0,.5-2.5c.011-.038.02-.078.03-.117a1.335,1.335,0,0,0,.166.017" transform="translate(-55.859 -57.894)" fill="#44d860" fill-rule="evenodd"/>
<path id="Path_62" data-name="Path 62" d="M141.8,38.745a1.41,1.41,0,0,1-.255-.026,1.309,1.309,0,0,1-.244-.073,1.349,1.349,0,0,1-.224-.119,1.967,1.967,0,0,1-.2-.161,1.52,1.52,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.41,1.41,0,0,1,.026-.255,1.5,1.5,0,0,1,.072-.244,1.364,1.364,0,0,1,.12-.223,1.252,1.252,0,0,1,.358-.358,1.349,1.349,0,0,1,.224-.119,1.309,1.309,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.968,1.968,0,0,1,.2.161,1.908,1.908,0,0,1,.161.2,1.322,1.322,0,0,1,.12.223,1.361,1.361,0,0,1,.1.5,1.317,1.317,0,0,1-.379.919,1.968,1.968,0,0,1-.2.161,1.346,1.346,0,0,1-.223.119,1.332,1.332,0,0,1-.5.1m10.389-.649a1.326,1.326,0,0,1-.92-.379,1.979,1.979,0,0,1-.161-.2,1.282,1.282,0,0,1-.218-.722,1.326,1.326,0,0,1,.379-.919,1.967,1.967,0,0,1,.2-.161,1.351,1.351,0,0,1,.224-.119,1.308,1.308,0,0,1,.244-.073,1.2,1.2,0,0,1,.509,0,1.262,1.262,0,0,1,.468.192,1.967,1.967,0,0,1,.2.161,1.326,1.326,0,0,1,.379.919,1.461,1.461,0,0,1-.026.255,1.323,1.323,0,0,1-.073.244,1.847,1.847,0,0,1-.119.223,1.911,1.911,0,0,1-.161.2,1.967,1.967,0,0,1-.2.161,1.294,1.294,0,0,1-.722.218" transform="translate(-69.074 -26.006)" fill-rule="evenodd"/>
</g>
<g id="React-icon" transform="translate(906.3 541.56)">
<path id="Path_330" data-name="Path 330" d="M263.668,117.179c0-5.827-7.3-11.35-18.487-14.775,2.582-11.4,1.434-20.477-3.622-23.382a7.861,7.861,0,0,0-4.016-1v4a4.152,4.152,0,0,1,2.044.466c2.439,1.4,3.5,6.724,2.672,13.574-.2,1.685-.52,3.461-.914,5.272a86.9,86.9,0,0,0-11.386-1.954,87.469,87.469,0,0,0-7.459-8.965c5.845-5.433,11.332-8.41,15.062-8.41V78h0c-4.931,0-11.386,3.514-17.913,9.611-6.527-6.061-12.982-9.539-17.913-9.539v4c3.712,0,9.216,2.959,15.062,8.356a84.687,84.687,0,0,0-7.405,8.947,83.732,83.732,0,0,0-11.4,1.972c-.412-1.793-.717-3.532-.932-5.2-.843-6.85.2-12.175,2.618-13.592a3.991,3.991,0,0,1,2.062-.466v-4h0a8,8,0,0,0-4.052,1c-5.039,2.9-6.168,11.96-3.568,23.328-11.153,3.443-18.415,8.947-18.415,14.757,0,5.828,7.3,11.35,18.487,14.775-2.582,11.4-1.434,20.477,3.622,23.382a7.882,7.882,0,0,0,4.034,1c4.931,0,11.386-3.514,17.913-9.611,6.527,6.061,12.982,9.539,17.913,9.539a8,8,0,0,0,4.052-1c5.039-2.9,6.168-11.96,3.568-23.328C256.406,128.511,263.668,122.988,263.668,117.179Zm-23.346-11.96c-.663,2.313-1.488,4.7-2.421,7.083-.735-1.434-1.506-2.869-2.349-4.3-.825-1.434-1.7-2.833-2.582-4.2C235.517,104.179,237.974,104.645,240.323,105.219Zm-8.212,19.1c-1.4,2.421-2.833,4.716-4.321,6.85-2.672.233-5.379.359-8.1.359-2.708,0-5.415-.126-8.069-.341q-2.232-3.2-4.339-6.814-2.044-3.523-3.73-7.136c1.112-2.4,2.367-4.805,3.712-7.154,1.4-2.421,2.833-4.716,4.321-6.85,2.672-.233,5.379-.359,8.1-.359,2.708,0,5.415.126,8.069.341q2.232,3.2,4.339,6.814,2.044,3.523,3.73,7.136C234.692,119.564,233.455,121.966,232.11,124.315Zm5.792-2.331c.968,2.4,1.793,4.805,2.474,7.136-2.349.574-4.823,1.058-7.387,1.434.879-1.381,1.757-2.8,2.582-4.25C236.4,124.871,237.167,123.419,237.9,121.984ZM219.72,141.116a73.921,73.921,0,0,1-4.985-5.738c1.614.072,3.263.126,4.931.126,1.685,0,3.353-.036,4.985-.126A69.993,69.993,0,0,1,219.72,141.116ZM206.38,130.555c-2.546-.377-5-.843-7.352-1.417.663-2.313,1.488-4.7,2.421-7.083.735,1.434,1.506,2.869,2.349,4.3S205.5,129.192,206.38,130.555ZM219.63,93.241a73.924,73.924,0,0,1,4.985,5.738c-1.614-.072-3.263-.126-4.931-.126-1.686,0-3.353.036-4.985.126A69.993,69.993,0,0,1,219.63,93.241ZM206.362,103.8c-.879,1.381-1.757,2.8-2.582,4.25-.825,1.434-1.6,2.869-2.331,4.3-.968-2.4-1.793-4.805-2.474-7.136C201.323,104.663,203.8,104.179,206.362,103.8Zm-16.227,22.449c-6.348-2.708-10.454-6.258-10.454-9.073s4.106-6.383,10.454-9.073c1.542-.663,3.228-1.255,4.967-1.811a86.122,86.122,0,0,0,4.034,10.92,84.9,84.9,0,0,0-3.981,10.866C193.38,127.525,191.694,126.915,190.134,126.252Zm9.647,25.623c-2.439-1.4-3.5-6.724-2.672-13.574.2-1.686.52-3.461.914-5.272a86.9,86.9,0,0,0,11.386,1.954,87.465,87.465,0,0,0,7.459,8.965c-5.845,5.433-11.332,8.41-15.062,8.41A4.279,4.279,0,0,1,199.781,151.875Zm42.532-13.663c.843,6.85-.2,12.175-2.618,13.592a3.99,3.99,0,0,1-2.062.466c-3.712,0-9.216-2.959-15.062-8.356a84.689,84.689,0,0,0,7.405-8.947,83.731,83.731,0,0,0,11.4-1.972A50.194,50.194,0,0,1,242.313,138.212Zm6.9-11.96c-1.542.663-3.228,1.255-4.967,1.811a86.12,86.12,0,0,0-4.034-10.92,84.9,84.9,0,0,0,3.981-10.866c1.775.556,3.461,1.165,5.039,1.829,6.348,2.708,10.454,6.258,10.454,9.073C259.67,119.994,255.564,123.562,249.216,126.252Z" fill="#61dafb"/>
<path id="Path_331" data-name="Path 331" d="M320.8,78.4Z" transform="translate(-119.082 -0.328)" fill="#61dafb"/>
<circle id="Ellipse_112" data-name="Ellipse 112" cx="8.194" cy="8.194" r="8.194" transform="translate(211.472 108.984)" fill="#61dafb"/>
<path id="Path_332" data-name="Path 332" d="M520.5,78.1Z" transform="translate(-282.975 -0.082)" fill="#61dafb"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,40 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1129" height="663" viewBox="0 0 1129 663">
<title>Focus on What Matters</title>
<circle cx="321" cy="321" r="321" fill="#f2f2f2" />
<ellipse cx="559" cy="635.49998" rx="514" ry="27.50002" fill="#3f3d56" />
<ellipse cx="558" cy="627" rx="460" ry="22" opacity="0.2" />
<rect x="131" y="152.5" width="840" height="50" fill="#3f3d56" />
<path d="M166.5,727.3299A21.67009,21.67009,0,0,0,188.1701,749H984.8299A21.67009,21.67009,0,0,0,1006.5,727.3299V296h-840Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
<path d="M984.8299,236H188.1701A21.67009,21.67009,0,0,0,166.5,257.6701V296h840V257.6701A21.67009,21.67009,0,0,0,984.8299,236Z" transform="translate(-35.5 -118.5)" opacity="0.2" />
<circle cx="181" cy="147.5" r="13" fill="#3f3d56" />
<circle cx="217" cy="147.5" r="13" fill="#3f3d56" />
<circle cx="253" cy="147.5" r="13" fill="#3f3d56" />
<rect x="168" y="213.5" width="337" height="386" rx="5.33505" fill="#606060" />
<rect x="603" y="272.5" width="284" height="22" rx="5.47638" fill="#2e8555" />
<rect x="537" y="352.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
<rect x="537" y="396.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
<rect x="537" y="440.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
<rect x="537" y="484.5" width="416" height="15" rx="5.47638" fill="#2e8555" />
<rect x="865" y="552.5" width="88" height="26" rx="7.02756" fill="#3ecc5f" />
<path d="M1088.60287,624.61594a30.11371,30.11371,0,0,0,3.98291-15.266c0-13.79652-8.54358-24.98081-19.08256-24.98081s-19.08256,11.18429-19.08256,24.98081a30.11411,30.11411,0,0,0,3.98291,15.266,31.248,31.248,0,0,0,0,30.53213,31.248,31.248,0,0,0,0,30.53208,31.248,31.248,0,0,0,0,30.53208,30.11408,30.11408,0,0,0-3.98291,15.266c0,13.79652,8.54353,24.98081,19.08256,24.98081s19.08256-11.18429,19.08256-24.98081a30.11368,30.11368,0,0,0-3.98291-15.266,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53208,31.248,31.248,0,0,0,0-30.53213Z" transform="translate(-35.5 -118.5)" fill="#3f3d56" />
<ellipse cx="1038.00321" cy="460.31783" rx="19.08256" ry="24.9808" fill="#3f3d56" />
<ellipse cx="1038.00321" cy="429.78574" rx="19.08256" ry="24.9808" fill="#3f3d56" />
<path d="M1144.93871,339.34489a91.61081,91.61081,0,0,0,7.10658-10.46092l-50.141-8.23491,54.22885.4033a91.566,91.566,0,0,0,1.74556-72.42605l-72.75449,37.74139,67.09658-49.32086a91.41255,91.41255,0,1,0-150.971,102.29805,91.45842,91.45842,0,0,0-10.42451,16.66946l65.0866,33.81447-69.40046-23.292a91.46011,91.46011,0,0,0,14.73837,85.83669,91.40575,91.40575,0,1,0,143.68892,0,91.41808,91.41808,0,0,0,0-113.02862Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M981.6885,395.8592a91.01343,91.01343,0,0,0,19.56129,56.51431,91.40575,91.40575,0,1,0,143.68892,0C1157.18982,436.82067,981.6885,385.60008,981.6885,395.8592Z" transform="translate(-35.5 -118.5)" opacity="0.1" />
<path d="M365.62,461.43628H477.094v45.12043H365.62Z" transform="translate(-35.5 -118.5)" fill="#fff" fill-rule="evenodd" />
<path d="M264.76252,608.74122a26.50931,26.50931,0,0,1-22.96231-13.27072,26.50976,26.50976,0,0,0,22.96231,39.81215H291.304V608.74122Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M384.17242,468.57061l92.92155-5.80726V449.49263a26.54091,26.54091,0,0,0-26.54143-26.54143H331.1161l-3.31768-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622-3.31767-5.74622a3.83043,3.83043,0,0,0-6.63536,0l-3.31768,5.74622L301.257,417.205a3.83043,3.83043,0,0,0-6.63536,0L291.304,422.9512c-.02919,0-.05573.004-.08625.004l-5.49674-5.49541a3.8293,3.8293,0,0,0-6.4071,1.71723l-1.81676,6.77338L270.607,424.1031a3.82993,3.82993,0,0,0-4.6912,4.69253l1.84463,6.89148-6.77072,1.81411a3.8315,3.8315,0,0,0-1.71988,6.40975l5.49673,5.49673c0,.02787-.004.05574-.004.08493l-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74621,3.31768L259.0163,466.081a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31767a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768L259.0163,558.976a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768-5.74622,3.31768a3.83042,3.83042,0,0,0,0,6.63535l5.74622,3.31768-5.74622,3.31768a3.83043,3.83043,0,0,0,0,6.63536l5.74622,3.31768A26.54091,26.54091,0,0,0,291.304,635.28265H450.55254A26.5409,26.5409,0,0,0,477.094,608.74122V502.5755l-92.92155-5.80727a14.12639,14.12639,0,0,1,0-28.19762" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M424.01111,635.28265h39.81214V582.19979H424.01111Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M490.36468,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15393-.59852A6.62668,6.62668,0,1,0,482.80568,590.21q-.2203-.22491-.44457-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39414-.10218-.59056-.15262a6.63957,6.63957,0,1,0-13.10086,0c-.1964.05042-.39414.09687-.59056.15262a6.62767,6.62767,0,1,0-11.39688,6.56369,26.52754,26.52754,0,1,0,44.23127,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
<path d="M437.28182,555.65836H477.094V529.11693H437.28182Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M490.36468,545.70532a3.31768,3.31768,0,0,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
<path d="M317.84538,466.081a3.31768,3.31768,0,0,1-3.31767-3.31768,9.953,9.953,0,1,0-19.90608,0,3.31768,3.31768,0,1,1-6.63535,0,16.58839,16.58839,0,1,1,33.17678,0,3.31768,3.31768,0,0,1-3.31768,3.31768" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
<path d="M370.92825,635.28265h79.62429A26.5409,26.5409,0,0,0,477.094,608.74122v-92.895H397.46968a26.54091,26.54091,0,0,0-26.54143,26.54143Z" transform="translate(-35.5 -118.5)" fill="#ffff50" fill-rule="evenodd" />
<path d="M457.21444,556.98543H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,1,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0-66.10674H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.29459H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414m0,26.54143H390.80778a1.32707,1.32707,0,0,1,0-2.65414h66.40666a1.32707,1.32707,0,0,1,0,2.65414M477.094,474.19076c-.01592,0-.0292-.008-.04512-.00663-4.10064.13934-6.04083,4.24132-7.75274,7.86024-1.78623,3.78215-3.16771,6.24122-5.43171,6.16691-2.50685-.09024-3.94007-2.92222-5.45825-5.91874-1.74377-3.44243-3.73438-7.34667-7.91333-7.20069-4.04227.138-5.98907,3.70784-7.70631,6.857-1.82738,3.35484-3.07084,5.39455-5.46887,5.30033-2.55727-.09289-3.91619-2.39536-5.48877-5.06013-1.75306-2.96733-3.77951-6.30359-7.8775-6.18946-3.97326.13669-5.92537,3.16507-7.64791,5.83912-1.82207,2.82666-3.09872,4.5492-5.52725,4.447-2.61832-.09289-3.9706-2.00388-5.53522-4.21611-1.757-2.4856-3.737-5.299-7.82308-5.16231-3.88567.13271-5.83779,2.61434-7.559,4.80135-1.635,2.07555-2.9116,3.71846-5.61218,3.615a1.32793,1.32793,0,1,0-.09555,2.65414c4.00377.134,6.03154-2.38873,7.79257-4.6275,1.562-1.9853,2.91027-3.69855,5.56441-3.78879,2.55594-.10882,3.75429,1.47968,5.56707,4.04093,1.7212,2.43385,3.67465,5.19416,7.60545,5.33616,4.11789.138,6.09921-2.93946,7.8536-5.66261,1.56861-2.43385,2.92221-4.53461,5.50734-4.62352,2.37944-.08892,3.67466,1.79154,5.50072,4.885,1.72121,2.91557,3.67069,6.21865,7.67977,6.36463,4.14709.14332,6.14965-3.47693,7.89475-6.68181,1.51155-2.77092,2.93814-5.38791,5.46621-5.4755,2.37944-.05573,3.62025,2.11668,5.45558,5.74622,1.71459,3.388,3.65875,7.22591,7.73019,7.37321l.22429.004c4.06614,0,5.99571-4.08074,7.70364-7.68905,1.51154-3.19825,2.94211-6.21069,5.3972-6.33411Z" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
<path d="M344.38682,635.28265h53.08286V582.19979H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M424.01111,602.10586a6.60242,6.60242,0,0,0-.848.08493c-.05042-.19906-.09821-.39945-.15394-.59852A6.62667,6.62667,0,1,0,416.45211,590.21q-.2203-.22491-.44458-.44589a6.62391,6.62391,0,1,0-11.39689-6.56369c-.1964-.05575-.39413-.10218-.59054-.15262a6.63957,6.63957,0,1,0-13.10084,0c-.19641.05042-.39414.09687-.59055.15262a6.62767,6.62767,0,1,0-11.39689,6.56369,26.52755,26.52755,0,1,0,44.2313,25.52756,6.6211,6.6211,0,1,0,.848-13.18579" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
<path d="M344.38682,555.65836h53.08286V529.11693H344.38682Z" transform="translate(-35.5 -118.5)" fill="#3ecc5f" fill-rule="evenodd" />
<path d="M410.74039,545.70532a3.31768,3.31768,0,1,0,0-6.63536,3.41133,3.41133,0,0,0-.42333.04247c-.02655-.09953-.04911-.19907-.077-.29859a3.319,3.319,0,0,0-1.278-6.37923,3.28174,3.28174,0,0,0-2.00122.68742q-.10947-.11346-.22294-.22295a3.282,3.282,0,0,0,.67149-1.98265,3.31768,3.31768,0,0,0-6.37-1.2992,13.27078,13.27078,0,1,0,0,25.54082,3.31768,3.31768,0,0,0,6.37-1.2992,3.282,3.282,0,0,0-.67149-1.98265q.11347-.10947.22294-.22294a3.28174,3.28174,0,0,0,2.00122.68742,3.31768,3.31768,0,0,0,1.278-6.37923c.02786-.0982.05042-.19907.077-.29859a3.41325,3.41325,0,0,0,.42333.04246" transform="translate(-35.5 -118.5)" fill="#44d860" fill-rule="evenodd" />
<path d="M424.01111,447.8338a3.60349,3.60349,0,0,1-.65028-.06636,3.34415,3.34415,0,0,1-.62372-.18579,3.44679,3.44679,0,0,1-.572-.30522,5.02708,5.02708,0,0,1-.50429-.4114,3.88726,3.88726,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.60248,3.60248,0,0,1,.06636-.65027,3.82638,3.82638,0,0,1,.18447-.62373,3.48858,3.48858,0,0,1,.30656-.57064,3.197,3.197,0,0,1,.91436-.91568,3.44685,3.44685,0,0,1,.572-.30523,3.344,3.344,0,0,1,.62372-.18578,3.06907,3.06907,0,0,1,1.30053,0,3.22332,3.22332,0,0,1,1.19436.491,5.02835,5.02835,0,0,1,.50429.41139,4.8801,4.8801,0,0,1,.41139.50429,3.38246,3.38246,0,0,1,.30522.57064,3.47806,3.47806,0,0,1,.25215,1.274A3.36394,3.36394,0,0,1,426.36,446.865a5.02708,5.02708,0,0,1-.50429.4114,3.3057,3.3057,0,0,1-1.84463.55737m26.54143-1.65884a3.38754,3.38754,0,0,1-2.35024-.96877,5.04185,5.04185,0,0,1-.41007-.50428,3.27532,3.27532,0,0,1-.55737-1.84463,3.38659,3.38659,0,0,1,.96744-2.34892,5.02559,5.02559,0,0,1,.50429-.41139,3.44685,3.44685,0,0,1,.572-.30523,3.3432,3.3432,0,0,1,.62373-.18579,3.06952,3.06952,0,0,1,1.30052,0,3.22356,3.22356,0,0,1,1.19436.491,5.02559,5.02559,0,0,1,.50429.41139,3.38792,3.38792,0,0,1,.96876,2.34892,3.72635,3.72635,0,0,1-.06636.65026,3.37387,3.37387,0,0,1-.18579.62373,4.71469,4.71469,0,0,1-.30522.57064,4.8801,4.8801,0,0,1-.41139.50429,5.02559,5.02559,0,0,1-.50429.41139,3.30547,3.30547,0,0,1-1.84463.55737" transform="translate(-35.5 -118.5)" fill-rule="evenodd" />
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

7
docs/tsconfig.json Normal file
View File

@@ -0,0 +1,7 @@
{
// This file is not used in compilation. It is here just for a nice editor experience.
"extends": "@docusaurus/tsconfig",
"compilerOptions": {
"baseUrl": ".",
},
}

5
examples/.dotdir/README.md Executable file
View File

@@ -0,0 +1,5 @@
# {{ name }} Readme
TO DO:
- [ ] ...

View File

@@ -1,17 +0,0 @@
import * as React from "react"
import * as css from "./{{Name}}.css"
class {{Name}} extends React.Component<any> {
private {{ property }}
constructor(props: any) {
super(props)
this.{{ property }} = {{ value }}
}
public render() {
return <div className={ css.{{Name}} } />
}
}
export default {{Name}}

View File

@@ -0,0 +1,17 @@
import * as React from "react"
import * as css from "./{{pascalCae name}}.css"
class {{pascalCae name}} extends React.Component<any> {
private {{ property }}
constructor(props: any) {
super(props)
this.{{ property }} = {{ value }}
}
public render() {
return <div className={ css.{{pascalCae name}} } />
}
}
export default {pascalCae nName}}

View File

@@ -1,13 +0,0 @@
/** @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

@@ -26,9 +26,7 @@ export default {
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
coveragePathIgnorePatterns: ["/node_modules/", "scaffold.config.js"],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
@@ -195,7 +193,7 @@ export default {
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
verbose: true,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],

6
nodemon.json Normal file
View File

@@ -0,0 +1,6 @@
{
"ignore": ["**/*.test.ts", "**/*.spec.ts", ".git", "node_modules"],
"watch": ["src"],
"exec": "node -r tsconfig-paths/register -r ts-node/register ./src/index.ts",
"ext": "ts, js"
}

View File

@@ -1,13 +1,14 @@
{
"name": "simple-scaffold",
"version": "1.5.0",
"version": "1.10.0",
"description": "Generate any file structure - from single components to entire app boilerplates, with a single command.",
"homepage": "https://chenasraf.github.io/simple-scaffold",
"repository": "https://github.com/chenasraf/simple-scaffold.git",
"author": "Chen Asraf <inbox@casraf.com>",
"author": "Chen Asraf <contact@casraf.dev>",
"license": "MIT",
"main": "index.js",
"bin": "cmd.js",
"packageManager": "pnpm@8.6.2",
"keywords": [
"javascript",
"cli",
@@ -20,47 +21,41 @@
"scaffolding"
],
"scripts": {
"clean": "rimraf dist/",
"build": "yarn clean && tsc && chmod -R +x ./dist && cp ./package.json ./README.md ./dist/",
"clean": "rm -rf dist/",
"build": "pnpm clean && tsc && chmod -R +x ./dist && cp ./package.json ./README.md ./dist/",
"dev": "tsc --watch",
"start": "node dist/scaffold.js",
"start": "ts-node src/scaffold.ts",
"test": "jest",
"cmd": "node --trace-warnings dist/cmd.js",
"build-test": "yarn build && yarn test",
"build-cmd": "yarn build && yarn cmd",
"build-docs": "typedoc",
"watch-docs": "yarn typedoc --watch",
"audit-fix": "npm_config_yes=true npx yarn-audit-fix --flow=convert",
"changelog": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s -r 0"
"cmd": "ts-node src/cmd.ts",
"docs:build": "cd docs && pnpm build",
"docs:watch": "cd docs && pnpm start",
"audit-fix": "pnpm audit --fix",
"changelog": "conventional-changelog -i CHANGELOG.md -s -r 0; echo \"# Change Log\n\n$(cat CHANGELOG.md)\" > CHANGELOG.md"
},
"dependencies": {
"chalk": "^4.1.2",
"date-fns": "^2.29.3",
"glob": "^9.2.1",
"handlebars": "^4.7.7",
"lodash": "^4.17.21",
"massarg": "^1.0.7-pre.1"
"date-fns": "^3.3.1",
"glob": "^10.3.10",
"handlebars": "^4.7.8",
"massarg": "2.0.0"
},
"devDependencies": {
"@knodes/typedoc-plugin-pages": "^0.23.4",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/release-notes-generator": "^10.0.3",
"@types/jest": "^29.5.1",
"@types/lodash": "^4.14.171",
"@types/mock-fs": "^4.13.1",
"@types/node": "^18.16.0",
"conventional-changelog": "^3.1.25",
"conventional-changelog-cli": "^2.2.2",
"conventional-changelog-conventionalcommits": "^5.0.0",
"jest": "^29.5.0",
"@semantic-release/release-notes-generator": "^12.1.0",
"@types/jest": "^29.5.11",
"@types/mock-fs": "^4.13.4",
"@types/node": "^20.11.14",
"@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",
"rimraf": "^5.0.0",
"semantic-release": "^21.0.1",
"semantic-release": "^23.0.0",
"semantic-release-conventional-commits": "^3.0.0",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"typedoc": "^0.24.6",
"typescript": "^5.0.4"
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
}

View File

@@ -1,7 +0,0 @@
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)

View File

@@ -1,79 +0,0 @@
## Available flags
```text
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 | |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--help`\|`-h` | Display help information |
| `--name`\|`-n` | Name to be passed to the generated files. {{name}} and {{Name}} inside contents and file names will be replaced accordingly. |
| `--config`\|`-c` | Filename or HTTPS git URL to load config from instead of passing arguments to CLI or using a Node.js script. |
| `--github`\|`-gh` | GitHub path to load config from instead of passing arguments to CLI or using a Node.js script. |
| `--key`\|`-k` | Key to load inside the config file. This overwrites the config key provided after the colon in --config (e.g. --config scaffold.cmd.js:component) |
| `--output`\|`-o` | Path to output to. If --create-sub-folder is enabled, the subfolder will be created inside this path. (default: current dir) |
| `--templates`\|`-t` | Template files to use as input. You may provide multiple files, each of which can be a relative or absolute path, or a glob pattern for multiple file matching easily. |
| `--overwrite`\|`-w` | Enable to override output files, even if they already exist. (default: false) |
| `--data`\|`-d` | Add custom data to the templates. By default, only your app name is included. |
| `--append-data`\|`-D` | Append additional custom data to the templates, which will overwrite --data, using an alternate syntax, which is easier to use with CLI: -D key1=string -D key2:=raw |
| `--create-sub-folder`\|`-s` | Create subfolder with the input name (default: false) |
| `--sub-folder-name-helper`\|`-sh` | Default helper to apply to subfolder name when using `--create-sub-folder true`. |
| `--quiet`\|`-q` | Suppress output logs (Same as --verbose 0) (default: false) |
| `--verbose`\|`-v` | Determine amount of logs to display. The values are: 0 (none) \| 1 (debug) \| 2 (info) \| 3 (warn) \| 4 (error). The provided level will display messages of the same level or higher. (default: 2) |
| `--dry-run`\|`-dr` | Don't emit files. This is good for testing your scaffolds and making sure they don't fail, without having to write actual file contents or create directories. (default: false) |
## Examples:
> See
> [Configuration Files](https://chenasraf.github.io/simple-scaffold/pages/docs/configuration_files.md)
> for organizing multiple scaffold types into easy-to-maintain files
Usage with config file
```shell
$ simple-scaffold -c scaffold.cmd.js --key component
```
Usage with GitHub config file
```shell
$ simple-scaffold -gh chenasraf/simple-scaffold --key component
```
Usage with https git URL (for non-GitHub)
```shell
$ simple-scaffold -c \
https://example.com/user/template.git#scaffold.cmd.js --key component
```
Full syntax with config path and template key (applicable to all above methods)
```shell
$ simple-scaffold -c scaffold.cmd.js:component MyComponent
```
Excluded template key, assumes 'default' key
```shell
$ simple-scaffold -c scaffold.cmd.js MyComponent
```
Shortest syntax for GitHub, assumes file 'scaffold.cmd.js' and template key 'default'
```shell
$ simple-scaffold -gh chenasraf/simple-scaffold MyComponent
```
You can also add this as a script in your `package.json`:
```json
{
"scripts": {
"scaffold": "npx simple-scaffold@latest -t scaffolds/component/**/* -o src/components -d '{\"myProp\": \"propName\", \"myVal\": 123}'"
}
}
```

View File

@@ -1,155 +0,0 @@
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
```
## Use In Node.js
You can also start a scaffold from Node.js with a remote file or URL config.
Just use the `Scaffold.fromConfig` function:
```ts
Scaffold.fromConfig(
"scaffold.config.js", // file or HTTPS git URL
{
// name of the generated component
name: "My Component",
// key to load from the config
key: "component",
},
{
// other config overrides
},
)
```

View File

@@ -1,25 +0,0 @@
In Simple Scaffold v1.0, the entire codebase was overhauled, yet usage remains mostly the same
between versions. With these notable exceptions:
- Some of the argument names have changed
- Template syntax has been improved
- The command to run Scaffold has been simplified from `new SimpleScaffold(opts).run()` to
`SimpleScaffold(opts)`, which now returns a promise that you can await to know when the process
has been completed.
## Argument changes
- `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
[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
Simple Scaffold still uses Handlebars.js to handle template content and file names. However, helpers
have been added to remove the need for you to pre-process the template data on simple use-cases such
as case type manipulation (converting to camel case, snake case, etc)
See the readme for the full information on how to use these helpers and which are available.

4576
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,76 +1,65 @@
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" },
]
const ref = process.env.GITHUB_REF || ""
const branch = ref.split("/").pop()
/** @type {import('semantic-release').Options} */
/**
* @type {import('semantic-release').GlobalConfig}
*/
module.exports = {
branches: [
"+([0-9])?(.{+([0-9]),x}).x",
"master",
"next",
"next-major",
{ name: "develop", prerelease: true },
{ name: "beta", prerelease: true },
{ name: "alpha", prerelease: true },
],
analyzeCommits: {
path: "semantic-release-conventional-commits",
majorTypes: ["major", "breaking"],
minorTypes: ["minor", "feat", "feature"],
patchTypes: ["patch", "fix", "bugfix", "refactor", "perf", "revert"],
},
branches: ["master", { name: "pre", prerelease: true }],
plugins: [
[
"@semantic-release/commit-analyzer",
{
preset: "conventionalcommits",
parserOpts: {
noteKeywords: ["breaking:", "breaking-fix:", "breaking-feat:"],
},
releaseRules: releaseRules,
},
],
[
"@semantic-release/release-notes-generator",
{
preset: "conventionalcommits",
parserOpts: {
noteKeywords: ["breaking"],
types: releaseRules,
},
},
],
[
"@semantic-release/changelog",
{
changelogFile: "CHANGELOG.md",
changelogTitle: "# Change Log",
},
],
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/npm",
{
pkgRoot: "dist",
// only update the pkg version on root, don't publish
npmPublish: false,
},
],
// [
// '@semantic-release/npm',
// {
// // only update the pkg version on doc, don't publish
// npmPublish: false,
// pkgRoot: 'doc',
// },
// ]
[
"@semantic-release/git",
"@semantic-release/npm",
{
assets: ["CHANGELOG.md", "package.json"],
// publish from dist dir instead of root
pkgRoot: "dist",
},
],
[
"@semantic-release/github",
{
assets: ["package.tgz"],
assets: ["*.tgz"],
},
],
[
"@semantic-release/git",
{
assets: ["package.json", "CHANGELOG.md"].filter(Boolean),
},
],
//
// [
// '@semantic-release/exec',
// {
// verifyReleaseCmd: 'echo ${nextRelease.version} > .VERSION',
// },
// ],
],
}
if (branch === "master") {
const gitIdx = module.exports.plugins.findIndex((plugin) => plugin[0] === "@semantic-release/git")
module.exports.plugins.splice(gitIdx, 0, [
"@semantic-release/changelog",
{
changelogFile: "CHANGELOG.md",
changelogTitle: "# Change Log",
},
])
}

16
scaffold.config.js Normal file
View File

@@ -0,0 +1,16 @@
/** @type {import('simple-scaffold').ScaffoldConfigFile} */
module.exports = (conf) => {
console.log("Config:", conf)
return {
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,167 +1,173 @@
#!/usr/bin/env node
import massarg from "massarg"
import { massarg } from "massarg"
import chalk from "chalk"
import { LogLevel, ScaffoldCmdConfig } from "./types"
import { Scaffold } from "./scaffold"
import path from "path"
import fs from "fs/promises"
import { parseAppendData, parseConfig } from "./utils"
import path from "node:path"
import fs from "node:fs/promises"
import { parseAppendData, parseConfigFile } from "./config"
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")
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 isConfigProvided =
args.includes("--config") || args.includes("-c") || args.includes("--git") || args.includes("-g")
return (
massarg<ScaffoldCmdConfig>()
.main(async (config) => {
const _config = await parseConfig(config)
return Scaffold(_config)
})
.option({
name: "name",
aliases: ["n"],
description:
"Name to be passed to the generated files. {{name}} and {{Name}} inside contents and file names will be replaced accordingly.",
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. See examples for syntax.",
})
.option({
name: "github",
aliases: ["gh"],
description:
"GitHub path to load config from instead of passing arguments to CLI or using a Node.js script. See examples for syntax.",
})
.option({
name: "key",
aliases: ["k"],
description:
"Key to load inside the config file. This overwrites the config key provided after the colon in --config (e.g. --config scaffold.cmd.js:component)",
})
.option({
name: "output",
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: !isConfig,
})
.option({
name: "templates",
aliases: ["t"],
array: true,
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: !isConfig,
})
.option({
name: "overwrite",
aliases: ["w"],
boolean: true,
defaultValue: false,
description: "Enable to override output files, even if they already exist.",
})
.option({
name: "data",
aliases: ["d"],
description: "Add custom data to the templates. By default, only your app name is included.",
parse: (v) => JSON.parse(v),
})
.option({
name: "append-data",
aliases: ["D"],
description:
"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",
parse: parseAppendData,
})
.option({
name: "create-sub-folder",
aliases: ["s"],
boolean: true,
defaultValue: false,
description: "Create subfolder with the input name",
})
.option({
name: "sub-folder-name-helper",
aliases: ["sh"],
description: "Default helper to apply to subfolder name when using `--create-sub-folder true`.",
})
.option({
name: "quiet",
aliases: ["q"],
boolean: true,
defaultValue: false,
description: "Suppress output logs (Same as --verbose 0)",
})
.option({
name: "verbose",
aliases: ["v"],
defaultValue: LogLevel.Info,
description:
"Determine amount of logs to display. The values are: " +
`${chalk.bold`0 (none) | 1 (debug) | 2 (info) | 3 (warn) | 4 (error)`}. ` +
"The provided level will display messages of the same level or higher.",
parse: Number,
})
.option({
name: "dry-run",
aliases: ["dr"],
boolean: true,
defaultValue: false,
description:
"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.",
})
// .example({
// input: `yarn cmd -t examples/test-input/Component -o examples/test-output -d '{"property":"myProp","value":"10"}'`,
// description: "Usage",
// output: "",
// })
.example({
description: "Usage with config file",
input: "simple-scaffold -c scaffold.cmd.js --key component",
})
.example({
description: "Usage with GitHub config file",
input: "simple-scaffold -gh chenasraf/simple-scaffold --key component",
})
.example({
description: "Usage with https git URL (for non-GitHub)",
input: "simple-scaffold -c https://example.com/user/template.git#scaffold.cmd.js --key component",
})
.example({
description: "Full syntax with config path and template key (applicable to all above methods)",
input: "simple-scaffold -c scaffold.cmd.js:component MyComponent",
})
.example({
description: "Excluded template key, assumes 'default' key",
input: "simple-scaffold -c scaffold.cmd.js MyComponent",
})
.example({
description: "Shortest syntax for GitHub, assumes file 'scaffold.cmd.js' and template key 'default'",
input: "simple-scaffold -gh chenasraf/simple-scaffold MyComponent",
})
.help({
binName: "simple-scaffold",
useGlobalColumns: true,
usageExample: "[options]",
printWidth: 100,
header: [`Create structured files based on templates.`].join("\n"),
footer: [
`Version: ${pkg.version}`,
`Copyright © Chen Asraf 2017-${new Date().getFullYear()}`,
``,
`Documentation: ${chalk.underline`https://chenasraf.github.io/simple-scaffold`}`,
`NPM: ${chalk.underline`https://npmjs.com/package/simple-scaffold`}`,
`GitHub: ${chalk.underline`https://github.com/chenasraf/simple-scaffold`}`,
].join("\n"),
})
.parse(args)
)
return massarg<ScaffoldCmdConfig>({
name: pkg.name,
description: pkg.description,
})
.main(async (config) => {
const parsed = await parseConfigFile(config)
return Scaffold(parsed)
})
.option({
name: "name",
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.",
isDefault: true,
required: true,
})
.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.",
})
.option({
name: "git",
aliases: ["g"],
description:
"Git URL to load config from instead of passing arguments to CLI or using a Node.js script. See " +
"examples for syntax.",
})
.option({
name: "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 `--subdir` is enabled, the subfolder will be created inside " +
"this path. Default is current working directory.",
required: !isConfigProvided,
})
.option({
name: "templates",
aliases: ["t"],
array: true,
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: !isConfigProvided,
})
.flag({
name: "overwrite",
aliases: ["w"],
defaultValue: false,
description: "Enable to override output files, even if they already exist.",
negatable: true,
})
.option({
name: "data",
aliases: ["d"],
description: "Add custom data to the templates. By default, only your app name is included.",
parse: (v) => JSON.parse(v),
})
.option({
name: "append-data",
aliases: ["D"],
description:
"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`",
parse: parseAppendData,
})
.flag({
name: "subdir",
aliases: ["s"],
defaultValue: false,
description: "Create a parent directory with the input name (and possibly `--subdir-helper`",
negatable: true,
negationName: "no-subdir",
})
.option({
name: "subdir-helper",
aliases: ["H"],
description: "Default helper to apply to subfolder name when using `--subdir`.",
})
.flag({
name: "quiet",
aliases: ["q"],
defaultValue: false,
description: "Suppress output logs (Same as `--log-level none`)",
})
.option({
name: "log-level",
aliases: ["l"],
defaultValue: LogLevel.info,
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: Number,
})
.flag({
name: "dry-run",
aliases: ["dr"],
defaultValue: false,
description:
"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.",
})
.example({
description: "Usage with config file",
input: "simple-scaffold -c scaffold.cmd.js --key component",
})
.example({
description: "Usage with GitHub config file",
input: "simple-scaffold -g chenasraf/simple-scaffold --key component",
})
.example({
description: "Usage with https git URL (for non-GitHub)",
input: "simple-scaffold -g https://example.com/user/template.git -c scaffold.cmd.js --key component",
})
.example({
description: "Excluded template key, assumes 'default' key",
input: "simple-scaffold -c scaffold.cmd.js MyComponent",
})
.example({
description:
"Shortest syntax for GitHub, searches for config file automaticlly, assumes and template key 'default'",
input: "simple-scaffold -g chenasraf/simple-scaffold MyComponent",
})
.help({
bindOption: true,
lineLength: 100,
useGlobalTableColumns: true,
usageText: [chalk.yellow`simple-scaffold`, chalk.gray`[options]`, chalk.cyan`<name>`].join(" "),
optionOptions: {
displayNegations: true,
},
footerText: [
`Version: ${pkg.version}`,
`Copyright © Chen Asraf 2017-${new Date().getFullYear()}`,
``,
`Documentation: ${chalk.underline`https://chenasraf.github.io/simple-scaffold`}`,
`NPM: ${chalk.underline`https://npmjs.com/package/simple-scaffold`}`,
`GitHub: ${chalk.underline`https://github.com/chenasraf/simple-scaffold`}`,
].join("\n"),
})
.parse(args)
}
parseCliArgs()

139
src/config.ts Normal file
View File

@@ -0,0 +1,139 @@
import path from "node:path"
import {
ConfigLoadConfig,
FileResponse,
FileResponseHandler,
LogConfig,
LogLevel,
RemoteConfigLoadConfig,
ScaffoldCmdConfig,
ScaffoldConfig,
ScaffoldConfigFile,
} from "./types"
import { handlebarsParse } from "./parser"
import { log } from "./logger"
import { resolve, wrapNoopResolver } from "./utils"
import { getGitConfig } from "./git"
/** @internal */
export function getOptionValueForFile<T>(
config: ScaffoldConfig,
filePath: string,
fn: FileResponse<T>,
defaultValue?: T,
): T {
if (typeof fn !== "function") {
return defaultValue ?? (fn as T)
}
return (fn as FileResponseHandler<T>)(
filePath,
path.dirname(handlebarsParse(config, filePath, { isPath: true }).toString()),
path.basename(handlebarsParse(config, filePath, { isPath: true }).toString()),
)
}
/** @internal */
export function parseAppendData(value: string, options: ScaffoldCmdConfig): unknown {
const data = options.data ?? {}
const [key, val] = value.split(/\:?=/)
// raw
if (value.includes(":=") && !val.includes(":=")) {
return { ...data, [key]: JSON.parse(val) }
}
return { ...data, [key]: isWrappedWithQuotes(val) ? val.substring(1, val.length - 1) : val }
}
function isWrappedWithQuotes(string: string): boolean {
return (string.startsWith('"') && string.endsWith('"')) || (string.startsWith("'") && string.endsWith("'"))
}
/** @internal */
export async function parseConfigFile(config: ScaffoldCmdConfig): Promise<ScaffoldConfig> {
let output: ScaffoldConfig = config
if (config.quiet) {
config.logLevel = LogLevel.none
}
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
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 ${configFilename} with key ${key}`)
const configPromise = await (config.git
? getRemoteConfig({ git: configPath, config: configFilename, logLevel: config.logLevel })
: 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) {
configImport = await resolve(configImport.default, config)
}
if (!configImport[key]) {
throw new Error(`Template "${key}" not found in ${configFilename}`)
}
const importedKey = configImport[key]
output = {
...config,
...importedKey,
data: {
...(importedKey as any).data,
...config.data,
},
}
}
output.data = { ...output.data, ...config.appendData }
delete config.appendData
return output
}
/** @internal */
export function githubPartToUrl(part: string): string {
const gitUrl = new URL(`https://github.com/${part}`)
if (!gitUrl.pathname.endsWith(".git")) {
gitUrl.pathname += ".git"
}
return gitUrl.toString()
}
/** @internal */
export async function getLocalConfig(config: ConfigLoadConfig & Partial<LogConfig>): Promise<ScaffoldConfigFile> {
const { config: configFile, ...logConfig } = config as Required<typeof config>
log(logConfig, LogLevel.info, `Loading config from file ${configFile}`)
const absolutePath = path.resolve(process.cwd(), configFile)
return wrapNoopResolver(import(absolutePath))
}
/** @internal */
export async function getRemoteConfig(
config: RemoteConfigLoadConfig & Partial<LogConfig>,
): Promise<ScaffoldConfigFile> {
const { config: configFile, git, ...logConfig } = config as Required<typeof config>
log(logConfig, LogLevel.info, `Loading config from remote ${git}, file ${configFile}`)
const url = new URL(git!)
const isHttp = url.protocol === "http:" || url.protocol === "https:"
const isGit = url.protocol === "git:" || (isHttp && url.pathname.endsWith(".git"))
if (!isGit) {
throw new Error(`Unsupported protocol ${url.protocol}`)
}
return getGitConfig(url, configFile, logConfig)
}

211
src/file.ts Normal file
View File

@@ -0,0 +1,211 @@
import path from "node:path"
import { F_OK } from "node:constants"
import { LogLevel, ScaffoldConfig } from "./types"
import fs from "node:fs/promises"
import { glob, hasMagic } from "glob"
import { log } from "./logger"
import { getOptionValueForFile } from "./config"
import { handlebarsParse } from "./parser"
import { handleErr } from "./utils"
const { stat, access, mkdir, readFile, writeFile } = fs
export async function createDirIfNotExists(dir: string, config: ScaffoldConfig): Promise<void> {
if (config.dryRun) {
log(config, LogLevel.info, `Dry Run. Not creating dir ${dir}`)
return
}
const parentDir = path.dirname(dir)
if (!(await pathExists(parentDir))) {
await createDirIfNotExists(parentDir, config)
}
if (!(await pathExists(dir))) {
try {
log(config, LogLevel.debug, `Creating dir ${dir}`)
await mkdir(dir)
return
} catch (e: any) {
if (e.code !== "EEXIST") {
throw e
}
return
}
}
}
export async function pathExists(filePath: string): Promise<boolean> {
try {
await access(filePath, F_OK)
return true
} catch (e: any) {
if (e.code === "ENOENT") {
return false
}
throw e
}
}
export async function isDir(path: string): Promise<boolean> {
const tplStat = await stat(path)
return tplStat.isDirectory()
}
export function removeGlob(template: string): string {
return path.normalize(template.replace(/\*/g, ""))
}
export function makeRelativePath(str: string): string {
return str.startsWith(path.sep) ? str.slice(1) : str
}
export function getBasePath(relPath: string): string {
return path
.resolve(process.cwd(), relPath)
.replace(process.cwd() + path.sep, "")
.replace(process.cwd(), "")
}
export async function getFileList(_config: ScaffoldConfig, template: string): Promise<string[]> {
template = template.replaceAll(/[\\]+/g, "/")
log(_config, LogLevel.debug, `Getting file list for ${template}`)
return (
await glob(template, {
dot: true,
nodir: true,
})
).map(path.normalize)
}
export interface GlobInfo {
nonGlobTemplate: string
origTemplate: string
isDirOrGlob: boolean
isGlob: boolean
template: string
}
export async function getTemplateGlobInfo(config: ScaffoldConfig, template: string): Promise<GlobInfo> {
const isGlob = hasMagic(template)
log(config, LogLevel.debug, "before isDir", "isGlob:", isGlob, template)
let _template = template
let nonGlobTemplate = isGlob ? removeGlob(template) : template
nonGlobTemplate = path.normalize(nonGlobTemplate)
const isDirOrGlob = isGlob ? true : await isDir(template)
const _shouldAddGlob = !isGlob && isDirOrGlob
log(config, LogLevel.debug, "after", { isDirOrGlob, _shouldAddGlob })
const origTemplate = template
if (_shouldAddGlob) {
_template = path.join(template, "**", "*")
}
return { nonGlobTemplate, origTemplate, isDirOrGlob, isGlob, template: _template }
}
export interface OutputFileInfo {
inputPath: string
outputPathOpt: string
outputDir: string
outputPath: string
exists: boolean
}
export async function getTemplateFileInfo(
config: ScaffoldConfig,
{ templatePath, basePath }: { templatePath: string; basePath: string },
): Promise<OutputFileInfo> {
const inputPath = path.resolve(process.cwd(), templatePath)
const outputPathOpt = getOptionValueForFile(config, inputPath, config.output)
const outputDir = getOutputDir(config, outputPathOpt, basePath)
const outputPath = handlebarsParse(config, path.join(outputDir, path.basename(inputPath)), {
isPath: true,
}).toString()
const exists = await pathExists(outputPath)
return { inputPath, outputPathOpt, outputDir, outputPath, exists }
}
export async function copyFileTransformed(
config: ScaffoldConfig,
{
exists,
overwrite,
outputPath,
inputPath,
}: {
exists: boolean
overwrite: boolean
outputPath: string
inputPath: string
},
): Promise<void> {
if (!exists || overwrite) {
if (exists && overwrite) {
log(config, LogLevel.info, `File ${outputPath} exists, overwriting`)
}
const templateBuffer = await readFile(inputPath)
const unprocessedOutputContents = handlebarsParse(config, templateBuffer)
const finalOutputContents =
(await config.beforeWrite?.(unprocessedOutputContents, templateBuffer, outputPath)) ?? unprocessedOutputContents
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())
}
} else if (exists) {
log(config, LogLevel.info, `File ${outputPath} already exists, skipping`)
}
}
export function getOutputDir(config: ScaffoldConfig, outputPathOpt: string, basePath: string): string {
return path.resolve(
process.cwd(),
...([
outputPathOpt,
basePath,
config.subdir
? config.subdirHelper
? handlebarsParse(config, `{{ ${config.subdirHelper} name }}`).toString()
: config.name
: undefined,
].filter(Boolean) as string[]),
)
}
export async function handleTemplateFile(
config: ScaffoldConfig,
{ templatePath, basePath }: { templatePath: string; basePath: string },
): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
const { inputPath, outputPathOpt, outputDir, outputPath, exists } = await getTemplateFileInfo(config, {
templatePath,
basePath,
})
const overwrite = getOptionValueForFile(config, inputPath, config.overwrite ?? false)
log(
config,
LogLevel.debug,
`\nParsing ${templatePath}`,
`\nBase path: ${basePath}`,
`\nFull input path: ${inputPath}`,
`\nOutput Path Opt: ${outputPathOpt}`,
`\nFull output dir: ${outputDir}`,
`\nFull output path: ${outputPath}`,
`\n`,
)
await createDirIfNotExists(path.dirname(outputPath), config)
log(config, LogLevel.info, `Writing to ${outputPath}`)
await copyFileTransformed(config, { exists, overwrite, outputPath, inputPath })
resolve()
} catch (e: any) {
handleErr(e)
reject(e)
}
})
}

82
src/git.ts Normal file
View File

@@ -0,0 +1,82 @@
import path from "node:path"
import os from "node:os"
import fs from "node:fs/promises"
import { log } from "./logger"
import { AsyncResolver, LogConfig, LogLevel, ScaffoldCmdConfig, ScaffoldConfigMap } from "./types"
import { spawn } from "node:child_process"
import { resolve, wrapNoopResolver } from "./utils"
export async function getGitConfig(
url: URL,
file: string,
logConfig: LogConfig,
): Promise<AsyncResolver<ScaffoldCmdConfig, ScaffoldConfigMap>> {
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((res, reject) => {
const clone = spawn("git", ["clone", "--recurse-submodules", "--depth", "1", repoUrl, tmpPath])
clone.on("error", reject)
clone.on("close", async (code) => {
if (code === 0) {
res(await loadGitConfig({ logConfig, url: repoUrl, file, tmpPath }))
return
}
reject(new Error(`Git clone failed with code ${code}`))
})
})
}
/** @internal */
export async function loadGitConfig({
logConfig,
url: repoUrl,
file,
tmpPath,
}: {
logConfig: LogConfig
url: string
file: string
tmpPath: string
}): Promise<AsyncResolver<ScaffoldCmdConfig, ScaffoldConfigMap>> {
log(logConfig, LogLevel.info, `Loading config from git repo: ${repoUrl}`)
const filename = file || (await findConfigFile(tmpPath))
const absolutePath = path.resolve(tmpPath, filename)
const loadedConfig = await resolve(async () => (await import(absolutePath)).default as ScaffoldConfigMap, logConfig)
log(logConfig, LogLevel.info, `Loaded config from git`)
log(logConfig, LogLevel.debug, `Raw config:`, loadedConfig)
const fixedConfig: ScaffoldConfigMap = {}
for (const [k, v] of Object.entries(loadedConfig)) {
fixedConfig[k] = {
...v,
templates: v.templates.map((t) => path.resolve(tmpPath, t)),
}
}
await fs.rm(tmpPath, { recursive: true })
return wrapNoopResolver(fixedConfig)
}
/** @internal */
export async function findConfigFile(root: string): Promise<string> {
const allowed = ["mjs", "cjs", "js", "json"].reduce((acc, ext) => {
acc.push(`scaffold.config.${ext}`)
acc.push(`scaffold.${ext}`)
return acc
}, [] as string[])
for (const file of allowed) {
const exists = await fs
.stat(path.resolve(root, file))
.then(() => true)
.catch(() => false)
if (exists) {
return file
}
}
throw new Error(`Could not find config file in git repo`)
}

70
src/logger.ts Normal file
View File

@@ -0,0 +1,70 @@
import { LogConfig, LogLevel, ScaffoldConfig } from "./types"
import chalk from "chalk"
export function log(config: LogConfig, level: LogLevel, ...obj: any[]): void {
const priority: Record<LogLevel, number> = {
[LogLevel.none]: 0,
[LogLevel.debug]: 1,
[LogLevel.info]: 2,
[LogLevel.warning]: 3,
[LogLevel.error]: 4,
}
if (config.logLevel === LogLevel.none || priority[level] < priority[config.logLevel ?? LogLevel.info]) {
return
}
const levelColor: Record<keyof typeof LogLevel, keyof typeof chalk> = {
[LogLevel.none]: "reset",
[LogLevel.debug]: "blue",
[LogLevel.info]: "dim",
[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]
logFn(
...obj.map((i) =>
i instanceof Error
? chalkFn(i, JSON.stringify(i, undefined, 1), i.stack)
: typeof i === "object"
? chalkFn(JSON.stringify(i, undefined, 1))
: chalkFn(i),
),
)
}
export function logInputFile(
config: ScaffoldConfig,
data: {
originalTemplate: string
relativePath: string
parsedTemplate: string
inputFilePath: string
nonGlobTemplate: string
basePath: string
isDirOrGlob: boolean
isGlob: boolean
},
): void {
log(config, LogLevel.debug, data)
}
export function logInitStep(config: ScaffoldConfig): void {
log(config, LogLevel.debug, "Full config:", {
name: config.name,
templates: config.templates,
output: config.output,
subdir: config.subdir,
data: config.data,
overwrite: config.overwrite,
subdirHelper: config.subdirHelper,
helpers: Object.keys(config.helpers ?? {}),
logLevel: config.logLevel,
dryRun: config.dryRun,
beforeWrite: config.beforeWrite,
} as Record<keyof ScaffoldConfig, unknown>)
log(config, LogLevel.info, "Data:", config.data)
}

127
src/parser.ts Normal file
View File

@@ -0,0 +1,127 @@
import path from "node:path"
import { DefaultHelpers, Helper, LogLevel, ScaffoldConfig } from "./types"
import Handlebars from "handlebars"
import dtAdd from "date-fns/add"
import dtFormat from "date-fns/format"
import dtParseISO from "date-fns/parseISO"
import { log } from "./logger"
import { Duration } from "date-fns"
const dateFns = {
add: dtAdd.add,
format: dtFormat.format,
parseISO: dtParseISO.parseISO,
}
export const defaultHelpers: Record<DefaultHelpers, Helper> = {
camelCase,
snakeCase,
startCase,
kebabCase,
hyphenCase: kebabCase,
pascalCase,
lowerCase: (text) => text.toLowerCase(),
upperCase: (text) => text.toUpperCase(),
now: nowHelper,
date: dateHelper,
}
function _dateHelper(date: Date, formatString: string): string
function _dateHelper(date: Date, formatString: string, durationDifference: number, durationType: keyof Duration): string
function _dateHelper(
date: Date,
formatString: string,
durationDifference?: number,
durationType?: keyof Duration,
): string {
if (durationType && durationDifference !== undefined) {
return dateFns.format(dateFns.add(date, { [durationType]: durationDifference }), formatString)
}
return dateFns.format(date, formatString)
}
export function nowHelper(formatString: string): string
export function nowHelper(formatString: string, durationDifference: number, durationType: keyof Duration): string
export function nowHelper(formatString: string, durationDifference?: number, durationType?: keyof Duration): string {
return _dateHelper(new Date(), formatString, durationDifference!, durationType!)
}
export function dateHelper(date: string, formatString: string): string
export function dateHelper(
date: string,
formatString: string,
durationDifference: number,
durationType: keyof Duration,
): string
export function dateHelper(
date: string,
formatString: string,
durationDifference?: number,
durationType?: keyof Duration,
): string {
return _dateHelper(dateFns.parseISO(date), formatString, durationDifference!, durationType!)
}
// splits by either non-alpha character or capital letter
function toWordParts(string: string): string[] {
return string.split(/(?=[A-Z])|[^a-zA-Z]/).filter((s) => s.length > 0)
}
function camelCase(s: string): string {
return toWordParts(s).reduce((acc, part, i) => {
if (i === 0) {
return part.toLowerCase()
}
return acc + part[0].toUpperCase() + part.slice(1).toLowerCase()
}, "")
}
function snakeCase(s: string): string {
return toWordParts(s).join("_").toLowerCase()
}
function kebabCase(s: string): string {
return toWordParts(s).join("-").toLowerCase()
}
function startCase(s: string): string {
return toWordParts(s)
.map((part) => part[0].toUpperCase() + part.slice(1).toLowerCase())
.join(" ")
}
function pascalCase(s: string): string {
return startCase(s).replace(/\s+/g, "")
}
export function registerHelpers(config: ScaffoldConfig): void {
const _helpers = { ...defaultHelpers, ...config.helpers }
for (const helperName in _helpers) {
log(config, LogLevel.debug, `Registering helper: ${helperName}`)
Handlebars.registerHelper(helperName, _helpers[helperName as keyof typeof _helpers])
}
}
export function handlebarsParse(
config: ScaffoldConfig,
templateBuffer: Buffer | string,
{ isPath = false }: { isPath?: boolean } = {},
): Buffer {
const { data } = config
try {
let str = templateBuffer.toString()
if (isPath) {
str = str.replace(/\\/g, "/")
}
const parser = Handlebars.compile(str, { noEscape: true })
let outputContents = parser(data)
if (isPath && path.sep !== "/") {
outputContents = outputContents.replace(/\//g, "\\")
}
return Buffer.from(outputContents)
} catch (e) {
log(config, LogLevel.debug, e)
log(config, LogLevel.warning, "Couldn't parse file with handlebars, returning original content")
return Buffer.from(templateBuffer)
}
}

91
src/scaffold.ts Executable file → Normal file
View File

@@ -4,29 +4,21 @@
*
* See [readme](README.md)
*/
import path from "path"
import path from "node:path"
import { handleErr, resolve } from "./utils"
import {
createDirIfNotExists,
getOptionValueForFile,
handleErr,
log,
pascalCase,
isDir,
removeGlob,
makeRelativePath,
registerHelpers,
getTemplateGlobInfo,
getFileList,
getBasePath,
copyFileTransformed,
getTemplateFileInfo,
logInitStep,
logInputFile,
parseConfig,
} from "./utils"
import { LogLevel, ScaffoldCmdConfig, ScaffoldConfig } from "./types"
import { OptionsBase } from "massarg/types"
handleTemplateFile,
} from "./file"
import { LogLevel, MinimalConfig, Resolver, ScaffoldCmdConfig, ScaffoldConfig } from "./types"
import { registerHelpers } from "./parser"
import { log, logInitStep, logInputFile } from "./logger"
import { parseConfigFile } from "./config"
/**
* Create a scaffold using given `options`.
@@ -65,7 +57,7 @@ export async function Scaffold(config: ScaffoldConfig): Promise<void> {
registerHelpers(config)
try {
config.data = { name: config.name, Name: pascalCase(config.name), ...config.data }
config.data = { name: config.name, ...config.data }
logInitStep(config)
for (let _template of config.templates) {
try {
@@ -74,6 +66,7 @@ export async function Scaffold(config: ScaffoldConfig): Promise<void> {
_template,
)
const files = await getFileList(config, template)
log(config, LogLevel.debug, "Iterating files", { files, template })
for (const inputFilePath of files) {
if (await isDir(inputFilePath)) {
continue
@@ -81,9 +74,9 @@ export async function Scaffold(config: ScaffoldConfig): Promise<void> {
const relPath = makeRelativePath(path.dirname(removeGlob(inputFilePath).replace(nonGlobTemplate, "")))
const basePath = getBasePath(relPath)
logInputFile(config, {
origTemplate,
relPath,
template,
originalTemplate: origTemplate,
relativePath: relPath,
parsedTemplate: template,
inputFilePath,
nonGlobTemplate,
basePath,
@@ -100,7 +93,7 @@ export async function Scaffold(config: ScaffoldConfig): Promise<void> {
}
}
} catch (e: any) {
log(config, LogLevel.Error, e)
log(config, LogLevel.error, e)
throw e
}
}
@@ -117,61 +110,27 @@ export async function Scaffold(config: ScaffoldConfig): Promise<void> {
* @return {Promise<void>} A promise that resolves when the scaffold is complete
*/
Scaffold.fromConfig = async function (
/** The path or URL to the config file */
pathOrUrl: string,
config: Pick<ScaffoldCmdConfig, "name" | "key">,
overrides?: Partial<Omit<ScaffoldConfig, "name">>,
/** Information needed before loading the config */
config: MinimalConfig,
/** Any overrides to the loaded config */
overrides?: Resolver<ScaffoldCmdConfig, Partial<Omit<ScaffoldConfig, "name">>>,
): Promise<void> {
const _cmdConfig: ScaffoldCmdConfig & OptionsBase = {
const _cmdConfig: ScaffoldCmdConfig = {
dryRun: false,
output: process.cwd(),
verbose: LogLevel.Info,
logLevel: LogLevel.info,
overwrite: false,
templates: [],
createSubFolder: false,
subdir: false,
quiet: false,
help: false,
extras: [],
config: pathOrUrl,
...config,
}
const _config = await parseConfig(_cmdConfig)
return Scaffold({ ..._config, ...overrides })
}
async function handleTemplateFile(
config: ScaffoldConfig,
{ templatePath, basePath }: { templatePath: string; basePath: string },
): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
const { inputPath, outputPathOpt, outputDir, outputPath, exists } = await getTemplateFileInfo(config, {
templatePath,
basePath,
})
const overwrite = getOptionValueForFile(config, inputPath, config.overwrite ?? false)
log(
config,
LogLevel.Debug,
`\nParsing ${templatePath}`,
`\nBase path: ${basePath}`,
`\nFull input path: ${inputPath}`,
`\nOutput Path Opt: ${outputPathOpt}`,
`\nFull output dir: ${outputDir}`,
`\nFull output path: ${outputPath}`,
`\n`,
)
await createDirIfNotExists(path.dirname(outputPath), config)
log(config, LogLevel.Info, `Writing to ${outputPath}`)
await copyFileTransformed(config, { exists, overwrite, outputPath, inputPath })
resolve()
} catch (e: any) {
handleErr(e)
reject(e)
}
})
const _overrides = resolve(overrides, _cmdConfig)
const _config = await parseConfigFile(_cmdConfig)
return Scaffold({ ..._config, ..._overrides })
}
export default Scaffold

View File

@@ -3,8 +3,8 @@ import { HelperDelegate } from "handlebars/runtime"
/**
* The config object for defining a scaffolding group.
*
* @see {@link https://chenasraf.github.io/simple-scaffold/pages/node.html | Node.js usage}
* @see {@link https://chenasraf.github.io/simple-scaffold/pages/cli.html | CLI usage}
* @see {@link https://chenasraf.github.io/simple-scaffold/docs/usage/node| Node.js usage}
* @see {@link https://chenasraf.github.io/simple-scaffold/docs/usage/cli| CLI usage}
* @see {@link DefaultHelpers}
* @see {@link CaseHelpers}
* @see {@link DateHelpers}
@@ -27,7 +27,7 @@ export interface ScaffoldConfig {
templates: string[]
/**
* Path to output to. If `createSubFolder` is `true`, the subfolder will be created inside this path.
* Path to output to. If `subdir` is `true`, the subfolder will be created inside this path.
*
* May also be a {@link FileResponseHandler} which returns a new output path to override the default one.
*
@@ -39,12 +39,12 @@ export interface ScaffoldConfig {
/**
* Whether to create subfolder with the input name.
*
* When `true`, you may also use {@link subFolderNameHelper} to determine a pre-process helper on
* When `true`, you may also use {@link subdirHelper} to determine a pre-process helper on
* the directory name.
*
* @default `false`
*/
createSubFolder?: boolean
subdir?: boolean
/**
* Add custom data to the templates. By default, only your app name is included as `{{name}}` and `{{Name}}`.
@@ -57,7 +57,7 @@ export interface ScaffoldConfig {
* Enable to override output files, even if they already exist.
*
* You may supply a function to this option, which can take the arguments `(fullPath, baseDir, baseName)` and returns
* a string, to return a dynamic path for each file.
* a boolean for each file.
*
* May also be a {@link FileResponseHandler} which returns a boolean value per file.
*
@@ -68,12 +68,6 @@ export interface ScaffoldConfig {
*/
overwrite?: FileResponse<boolean>
/**
* Suppress output logs (Same as `verbose: 0` or `verbose: LogLevel.None`)
* @see {@link verbose}
*/
quiet?: boolean
/**
* Determine amount of logs to display.
*
@@ -84,7 +78,7 @@ export interface ScaffoldConfig {
*
* @default `2 (info)`
*/
verbose?: LogLevel
logLevel?: LogLevel
/**
* Don't emit files. This is good for testing your scaffolds and making sure they don't fail, without having to write
@@ -132,20 +126,20 @@ export interface ScaffoldConfig {
* @see {@link DefaultHelpers}
* @see {@link CaseHelpers}
* @see {@link DateHelpers}
* @see {@link https://chenasraf.github.io/simple-scaffold/pages/templates.html | Templates}
* @see {@link https://chenasraf.github.io/simple-scaffold/docs/usage/templates| Templates}
* */
helpers?: Record<string, Helper>
/**
* Default transformer to apply to subfolder name when using `createSubFolder: true`. Can be one of the default
* Default transformer to apply to subfolder name when using `subdir: true`. Can be one of the default
* capitalization helpers, or a custom one you provide to `helpers`. Defaults to `undefined`, which means no
* transformation is done.
*
* @see {@link createSubFolder}
* @see {@link subdir}
* @see {@link CaseHelpers}
* @see {@link DefaultHelpers}
*/
subFolderNameHelper?: DefaultHelpers | string
subdirHelper?: DefaultHelpers | string
/**
* This callback runs right before content is being written to the disk. If you supply this function, you may return
@@ -188,7 +182,7 @@ export interface ScaffoldConfig {
* @see {@link DefaultHelpers}
* @see {@link DateHelpers}
* @see {@link ScaffoldConfig}
* @see {@link ScaffoldConfig.subFolderNameHelper}
* @see {@link ScaffoldConfig.subdirHelper}
*
* @category Helpers
*/
@@ -263,6 +257,23 @@ export type DefaultHelpers = CaseHelpers | DateHelpers
*/
export type Helper = HelperDelegate
export const LogLevel = {
/** Silent output */
none: "none",
/** Debugging information. Very verbose and only recommended for troubleshooting. */
debug: "debug",
/**
* The regular level of logging. Major actions are logged to show the scaffold progress.
*
* @default
*/
info: "info",
/** Warnings such as when file fails to replace token values properly in template. */
warning: "warning",
/** Errors, such as missing files, bad replacement token syntax, or un-writable directories. */
error: "error",
} as const
/**
* The amount of information to log when generating scaffold.
* When not `None`, the selected level will be the lowest level included.
@@ -270,26 +281,14 @@ export type Helper = HelperDelegate
* For example, level `Info` (2) will include `Info`, `Warning` and `Error`, but not `Debug`; and `Warning` will only
* show `Warning` and `Error`.
*
* You may use either the number or the name of the level.
* For example, `2` or `info` are both valid.
*
* @default `2 (info)`
*
* @category Logging
*/
export enum LogLevel {
/** Silent output */
None = 0,
/** Debugging information. Very verbose and only recommended for troubleshooting. */
Debug = 1,
/**
* The regular level of logging. Major actions are logged to show the scaffold progress.
*
* @default
*/
Info = 2,
/** Warnings such as when file fails to replace token values properly in template. */
Warning = 3,
/** Errors, such as missing files, bad replacement token syntax, or un-writable directories. */
Error = 4,
}
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]
/**
* A function that takes path information about file, and returns a value of type `T`
@@ -315,29 +314,49 @@ export type FileResponseHandler<T> = (fullPath: string, basedir: string, basenam
* (fullPath: string, basedir: string, basename: string) => T
* ```
*
* @typedef T The return type
*
* @see {@link FileResponseHandler}
*
* @category Config
* */
export type FileResponse<T> = T | FileResponseHandler<T>
/** @internal */
/**
* The Scaffold config for CLI
* Contains less and more specific options than {@link ScaffoldConfig}
*/
export interface ScaffoldCmdConfig {
/** The name of the scaffold template to use. */
name: string
/** The templates to use for generation */
templates: string[]
/** The output path to write to */
output: string
createSubFolder: boolean
/** Whether to create subfolder with the input name */
subdir: boolean
/** Default transformer to apply to subfolder name when using `subdir: true` */
subdirHelper?: string
/** Add custom data to the templates */
data?: Record<string, string>
/** Add custom data to the template in a CLI-friendly syntax (and not JSON) */
appendData?: Record<string, string>
/** Enable to override output files, even if they already exist */
overwrite: boolean
/** Silence logs, same as `logLevel: "none"` */
quiet: boolean
verbose: LogLevel
/**
* Determine amount of logs to display.
*
* @see {@link LogLevel}
*/
logLevel: LogLevel
/** 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. */
dryRun: boolean
/** Config file path to use */
config?: string
/** The key of the template to use */
key?: string
github?: string
/** The git repository to use to fetch the config file */
git?: string
}
/**
@@ -351,4 +370,30 @@ export interface ScaffoldCmdConfig {
*
* @see {@link ScaffoldConfig}
*/
export type ScaffoldConfigFile = Record<string, ScaffoldConfig>
export type ScaffoldConfigMap = Record<string, ScaffoldConfig>
/** The scaffold config file is either:
* - A {@link ScaffoldConfigMap} object
* - A function that returns a {@link ScaffoldConfigMap} object
* - A promise that resolves to a {@link ScaffoldConfigMap} object
* - A function that returns a promise that resolves to a {@link ScaffoldConfigMap} object
*/
export type ScaffoldConfigFile = AsyncResolver<ScaffoldCmdConfig, ScaffoldConfigMap>
/** @internal */
export type Resolver<T, R = T> = R | ((value: T) => R)
/** @internal */
export type AsyncResolver<T, R = T> = Resolver<T, Promise<R> | R>
/** @internal */
export type LogConfig = Pick<ScaffoldConfig, "logLevel">
/** @internal */
export type ConfigLoadConfig = LogConfig & Pick<ScaffoldCmdConfig, "config">
/** @internal */
export type RemoteConfigLoadConfig = LogConfig & Pick<ScaffoldCmdConfig, "config" | "git">
/** @internal */
export type MinimalConfig = Pick<ScaffoldCmdConfig, "name" | "key">

View File

@@ -1,508 +1,17 @@
import path from "path"
import { F_OK } from "constants"
import {
DefaultHelpers,
FileResponse,
FileResponseHandler,
Helper,
LogLevel,
ScaffoldCmdConfig,
ScaffoldConfig,
ScaffoldConfigFile,
} from "./types"
import camelCase from "lodash/camelCase"
import snakeCase from "lodash/snakeCase"
import kebabCase from "lodash/kebabCase"
import startCase from "lodash/startCase"
import Handlebars from "handlebars"
import { promises as fsPromises } from "fs"
import chalk from "chalk"
const { stat, access, mkdir } = fsPromises
import dtAdd from "date-fns/add"
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,
format: dtFormat,
parseISO: dtParseISO,
}
const { readFile, writeFile } = fsPromises
export const defaultHelpers: Record<DefaultHelpers, Helper> = {
camelCase,
snakeCase,
startCase,
kebabCase,
hyphenCase: kebabCase,
pascalCase,
lowerCase: (text) => text.toLowerCase(),
upperCase: (text) => text.toUpperCase(),
now: nowHelper,
date: dateHelper,
}
export function _dateHelper(date: Date, formatString: string): string
export function _dateHelper(
date: Date,
formatString: string,
durationDifference: number,
durationType: keyof Duration,
): string
export function _dateHelper(
date: Date,
formatString: string,
durationDifference?: number,
durationType?: keyof Duration,
): string {
if (durationType && durationDifference !== undefined) {
return dateFns.format(dateFns.add(date, { [durationType]: durationDifference }), formatString)
}
return dateFns.format(date, formatString)
}
export function nowHelper(formatString: string): string
export function nowHelper(formatString: string, durationDifference: number, durationType: keyof Duration): string
export function nowHelper(formatString: string, durationDifference?: number, durationType?: keyof Duration): string {
return _dateHelper(new Date(), formatString, durationDifference!, durationType!)
}
export function dateHelper(date: string, formatString: string): string
export function dateHelper(
date: string,
formatString: string,
durationDifference: number,
durationType: keyof Duration,
): string
export function dateHelper(
date: string,
formatString: string,
durationDifference?: number,
durationType?: keyof Duration,
): string {
return _dateHelper(dateFns.parseISO(date), formatString, durationDifference!, durationType!)
}
export function registerHelpers(config: ScaffoldConfig): void {
const _helpers = { ...defaultHelpers, ...config.helpers }
for (const helperName in _helpers) {
log(config, LogLevel.Debug, `Registering helper: ${helperName}`)
Handlebars.registerHelper(helperName, _helpers[helperName as keyof typeof _helpers])
}
}
import { Resolver } from "./types"
export function handleErr(err: NodeJS.ErrnoException | null): void {
if (err) throw err
}
/** @internal */
export type LogConfig = Pick<ScaffoldConfig, "quiet" | "verbose">
export function resolve<T, R = T>(resolver: Resolver<T, R>, arg: T): R {
return typeof resolver === "function" ? (resolver as (value: T) => R)(arg) : (resolver as R)
}
export function log(config: LogConfig, level: LogLevel, ...obj: any[]): void {
if (config.quiet || config.verbose === LogLevel.None || level < (config.verbose ?? LogLevel.Info)) {
return
export function wrapNoopResolver<T, R = T>(value: Resolver<T, R>): Resolver<T, R> {
if (typeof value === "function") {
return value
}
const levelColor: Record<LogLevel, keyof typeof chalk> = {
[LogLevel.None]: "reset",
[LogLevel.Debug]: "blue",
[LogLevel.Info]: "dim",
[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]
logFn(
...obj.map((i) =>
i instanceof Error
? chalkFn(i, JSON.stringify(i, undefined, 1), i.stack)
: typeof i === "object"
? chalkFn(JSON.stringify(i, undefined, 1))
: chalkFn(i),
),
)
}
export async function createDirIfNotExists(dir: string, config: ScaffoldConfig): Promise<void> {
const parentDir = path.dirname(dir)
if (!(await pathExists(parentDir))) {
await createDirIfNotExists(parentDir, config)
}
if (!(await pathExists(dir))) {
try {
log(config, LogLevel.Debug, `Creating dir ${dir}`)
await mkdir(dir)
return
} catch (e: any) {
if (e.code !== "EEXIST") {
throw e
}
return
}
}
}
export function getOptionValueForFile<T>(
config: ScaffoldConfig,
filePath: string,
fn: FileResponse<T>,
defaultValue?: T,
): T {
if (typeof fn !== "function") {
return defaultValue ?? (fn as T)
}
return (fn as FileResponseHandler<T>)(
filePath,
path.dirname(handlebarsParse(config, filePath, { isPath: true }).toString()),
path.basename(handlebarsParse(config, filePath, { isPath: true }).toString()),
)
}
export function handlebarsParse(
config: ScaffoldConfig,
templateBuffer: Buffer | string,
{ isPath = false }: { isPath?: boolean } = {},
): Buffer {
const { data } = config
try {
let str = templateBuffer.toString()
if (isPath) {
str = str.replace(/\\/g, "/")
}
const parser = Handlebars.compile(str, { noEscape: true })
let outputContents = parser(data)
if (isPath && path.sep !== "/") {
outputContents = outputContents.replace(/\//g, "\\")
}
return Buffer.from(outputContents)
} catch (e) {
log(config, LogLevel.Debug, e)
log(config, LogLevel.Warning, "Couldn't parse file with handlebars, returning original content")
return Buffer.from(templateBuffer)
}
}
export async function pathExists(filePath: string): Promise<boolean> {
try {
await access(filePath, F_OK)
return true
} catch (e: any) {
if (e.code === "ENOENT") {
return false
}
throw e
}
}
export function pascalCase(s: string): string {
return startCase(s).replace(/\s+/g, "")
}
export async function isDir(path: string): Promise<boolean> {
const tplStat = await stat(path)
return tplStat.isDirectory()
}
export function removeGlob(template: string): string {
return template.replace(/\*/g, "").replace(/(\/\/|\\\\)/g, path.sep)
}
export function makeRelativePath(str: string): string {
return str.startsWith(path.sep) ? str.slice(1) : str
}
export function getBasePath(relPath: string): string {
return path
.resolve(process.cwd(), relPath)
.replace(process.cwd() + path.sep, "")
.replace(process.cwd(), "")
}
export async function getFileList(config: ScaffoldConfig, template: string): Promise<string[]> {
return (
await glob(template, {
dot: true,
nodir: true,
// debug: config.verbose === LogLevel.Debug,
})
).map((f) => f.replace(/\//g, path.sep))
}
export interface GlobInfo {
nonGlobTemplate: string
origTemplate: string
isDirOrGlob: boolean
isGlob: boolean
template: string
}
export async function getTemplateGlobInfo(config: ScaffoldConfig, template: string): Promise<GlobInfo> {
const isGlob = hasMagic(template)
log(config, LogLevel.Debug, "before isDir", "isGlob:", isGlob, template)
let _template = template
let nonGlobTemplate = isGlob ? removeGlob(template) : template
nonGlobTemplate = path.normalize(nonGlobTemplate)
const isDirOrGlob = isGlob ? true : await isDir(template)
log(config, LogLevel.Debug, "after isDir", isDirOrGlob)
const _shouldAddGlob = !isGlob && isDirOrGlob
const origTemplate = template
if (_shouldAddGlob) {
_template = path.join(template, "**", "*")
}
return { nonGlobTemplate, origTemplate, isDirOrGlob, isGlob, template: _template }
}
export interface OutputFileInfo {
inputPath: string
outputPathOpt: string
outputDir: string
outputPath: string
exists: boolean
}
export async function getTemplateFileInfo(
config: ScaffoldConfig,
{ templatePath, basePath }: { templatePath: string; basePath: string },
): Promise<OutputFileInfo> {
const inputPath = path.resolve(process.cwd(), templatePath)
const outputPathOpt = getOptionValueForFile(config, inputPath, config.output)
const outputDir = getOutputDir(config, outputPathOpt, basePath)
const outputPath = handlebarsParse(config, path.join(outputDir, path.basename(inputPath)), {
isPath: true,
}).toString()
const exists = await pathExists(outputPath)
return { inputPath, outputPathOpt, outputDir, outputPath, exists }
}
export async function copyFileTransformed(
config: ScaffoldConfig,
{
exists,
overwrite,
outputPath,
inputPath,
}: {
exists: boolean
overwrite: boolean
outputPath: string
inputPath: string
},
): Promise<void> {
if (!exists || overwrite) {
if (exists && overwrite) {
log(config, LogLevel.Info, `File ${outputPath} exists, overwriting`)
}
const templateBuffer = await readFile(inputPath)
const unprocessedOutputContents = handlebarsParse(config, templateBuffer)
const finalOutputContents =
(await config.beforeWrite?.(unprocessedOutputContents, templateBuffer, outputPath)) ?? unprocessedOutputContents
if (!config.dryRun) {
await writeFile(outputPath, finalOutputContents)
log(config, LogLevel.Info, "Done.")
} else {
log(config, LogLevel.Info, "Content output:")
log(config, LogLevel.Info, finalOutputContents)
}
} else if (exists) {
log(config, LogLevel.Info, `File ${outputPath} already exists, skipping`)
}
}
export function getOutputDir(config: ScaffoldConfig, outputPathOpt: string, basePath: string): string {
return path.resolve(
process.cwd(),
...([
outputPathOpt,
basePath,
config.createSubFolder
? config.subFolderNameHelper
? handlebarsParse(config, `{{ ${config.subFolderNameHelper} name }}`).toString()
: config.name
: undefined,
].filter(Boolean) as string[]),
)
}
export function logInputFile(
config: ScaffoldConfig,
{
origTemplate,
relPath,
template,
inputFilePath,
nonGlobTemplate,
basePath,
isDirOrGlob,
isGlob,
}: {
origTemplate: string
relPath: string
template: string
inputFilePath: string
nonGlobTemplate: string
basePath: string
isDirOrGlob: boolean
isGlob: boolean
},
): void {
log(
config,
LogLevel.Debug,
`\nprocess.cwd(): ${process.cwd()}`,
`\norigTemplate: ${origTemplate}`,
`\nrelPath: ${relPath}`,
`\ntemplate: ${template}`,
`\ninputFilePath: ${inputFilePath}`,
`\nnonGlobTemplate: ${nonGlobTemplate}`,
`\nbasePath: ${basePath}`,
`\nisDirOrGlob: ${isDirOrGlob}`,
`\nisGlob: ${isGlob}`,
`\n`,
)
}
export function logInitStep(config: ScaffoldConfig): void {
log(config, LogLevel.Debug, "Full config:", {
name: config.name,
templates: config.templates,
output: config.output,
createSubFolder: config.createSubFolder,
data: config.data,
overwrite: config.overwrite,
quiet: config.quiet,
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)
}
export function parseAppendData(value: string, options: ScaffoldCmdConfig & OptionsBase): unknown {
const data = options.data ?? {}
const [key, val] = value.split(/\:?=/)
// raw
if (value.includes(":=") && !val.includes(":=")) {
return { ...data, [key]: JSON.parse(val) }
}
return { ...data, [key]: isWrappedWithQuotes(val) ? val.substring(1, val.length - 1) : val }
}
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
return (_) => value
}

158
tests/config.test.ts Normal file
View File

@@ -0,0 +1,158 @@
import mockFs from "mock-fs"
import FileSystem from "mock-fs/lib/filesystem"
import { Console } from "console"
import { LogLevel, ScaffoldCmdConfig } from "../src/types"
import * as config from "../src/config"
import { resolve } from "../src/utils"
// @ts-ignore
import * as configFile from "../scaffold.config"
import { findConfigFile } from "../src/git"
jest.mock("../src/git", () => {
return {
__esModule: true,
...jest.requireActual("../src/git"),
getGitConfig: () => {
return Promise.resolve(blankCliConf)
},
}
})
const { githubPartToUrl, parseAppendData, parseConfigFile } = config
const blankCliConf: ScaffoldCmdConfig = {
logLevel: LogLevel.none,
name: "",
output: "",
templates: [],
data: { name: "test" },
overwrite: false,
subdir: false,
dryRun: false,
quiet: false,
}
const blankConfig: ScaffoldCmdConfig = {
...blankCliConf,
data: {},
}
describe("config", () => {
describe("parseAppendData", () => {
test('works for "key=value"', () => {
expect(parseAppendData("key=value", blankCliConf)).toEqual({ key: "value", name: "test" })
})
test('works for "key:=value"', () => {
expect(parseAppendData("key:=123", blankCliConf)).toEqual({ key: 123, name: "test" })
})
test("overwrites existing value", () => {
expect(parseAppendData("name:=123", blankCliConf)).toEqual({ name: 123 })
})
test("works with quotes", () => {
expect(parseAppendData('key="value test"', blankCliConf)).toEqual({ key: "value test", name: "test" })
})
})
describe("githubPartToUrl", () => {
test("works", () => {
expect(githubPartToUrl("chenasraf/simple-scaffold")).toEqual("https://github.com/chenasraf/simple-scaffold.git")
expect(githubPartToUrl("chenasraf/simple-scaffold.git")).toEqual(
"https://github.com/chenasraf/simple-scaffold.git",
)
})
})
describe("parseConfigFile", () => {
test("normal config does not change", async () => {
expect(
await parseConfigFile({
...blankCliConf,
}),
).toEqual(blankCliConf)
})
describe("appendData", () => {
test("appends", async () => {
const result = await parseConfigFile({
...blankCliConf,
appendData: { key: "value" },
})
expect(result?.data?.key).toEqual("value")
})
test("overwrites existing value", async () => {
const result = await parseConfigFile({
...blankCliConf,
data: { num: "123" },
appendData: { num: "1234" },
})
expect(result?.data?.num).toEqual("1234")
})
})
})
describe("getConfig", () => {
test("gets git config", async () => {
const resultFn = await config.getRemoteConfig({
git: "https://github.com/chenasraf/simple-scaffold.git",
logLevel: LogLevel.none,
})
const result = await resolve(resultFn, blankCliConf)
expect(result).toEqual(blankCliConf)
})
test("gets local file config", async () => {
const resultFn = await config.getLocalConfig({
config: "scaffold.config.js",
logLevel: LogLevel.none,
})
const result = await resolve(resultFn, {} as any)
expect(result).toEqual(configFile)
})
})
describe("findConfigFile", () => {
const struct1 = {
"scaffold.config.js": `module.exports = '${JSON.stringify(blankConfig)}'`,
}
const struct2 = {
"scaffold.js": `module.exports = '${JSON.stringify(blankConfig)}'`,
}
const struct3 = {
"scaffold.cjs": `module.exports = '${JSON.stringify(blankConfig)}'`,
}
const struct4 = {
"scaffold.json": JSON.stringify(blankConfig),
}
function withMock(fileStruct: FileSystem.DirectoryItems, testFn: jest.EmptyFunction): jest.EmptyFunction {
return () => {
beforeEach(() => {
// console.log("Mocking:", fileStruct)
console = new Console(process.stdout, process.stderr)
mockFs(fileStruct)
// logMock = jest.spyOn(console, 'log').mockImplementation((...args) => {
// logsTemp.push(args)
// })
})
testFn()
afterEach(() => {
// console.log("Restoring mock")
mockFs.restore()
})
}
}
for (const struct of [struct1, struct2, struct3, struct4]) {
const [k] = Object.keys(struct)
describe(`finds config file ${k}`, () => {
withMock(struct, async () => {
const result = await findConfigFile(process.cwd())
expect(result).toEqual(k)
})
})
}
})
})

133
tests/parser.test.ts Normal file
View File

@@ -0,0 +1,133 @@
import { ScaffoldConfig } from "../src/types"
import path from "node:path"
import * as dateFns from "date-fns"
import { dateHelper, defaultHelpers, handlebarsParse, nowHelper } from "../src/parser"
const blankConf: ScaffoldConfig = {
logLevel: "none",
name: "",
output: "",
templates: [],
data: { name: "test" },
}
describe("parser", () => {
describe("handlebarsParse", () => {
let origSep: any
describe("windows paths", () => {
beforeAll(() => {
origSep = path.sep
Object.defineProperty(path, "sep", { value: "\\" })
})
afterAll(() => {
Object.defineProperty(path, "sep", { value: origSep })
})
test("should work for windows paths", async () => {
expect(handlebarsParse(blankConf, "C:\\exports\\{{name}}.txt", { isPath: true }).toString()).toEqual(
"C:\\exports\\test.txt",
)
})
})
describe("non-windows paths", () => {
beforeAll(() => {
origSep = path.sep
Object.defineProperty(path, "sep", { value: "/" })
})
afterAll(() => {
Object.defineProperty(path, "sep", { value: origSep })
})
test("should work for non-windows paths", async () => {
expect(handlebarsParse(blankConf, "/home/test/{{name}}.txt", { isPath: true })).toEqual(
Buffer.from("/home/test/test.txt"),
)
})
})
test("should not do path escaping on non-path compiles", async () => {
expect(
handlebarsParse(
{ ...blankConf, data: { ...blankConf.data, escaped: "value" } },
"/home/test/{{name}} \\{{escaped}}.txt",
{
isPath: false,
},
),
).toEqual(Buffer.from("/home/test/test {{escaped}}.txt"))
})
})
describe("Helpers", () => {
describe("string helpers", () => {
test("camelCase", () => {
expect(defaultHelpers.camelCase("test string")).toEqual("testString")
expect(defaultHelpers.camelCase("test_string")).toEqual("testString")
expect(defaultHelpers.camelCase("test-string")).toEqual("testString")
expect(defaultHelpers.camelCase("testString")).toEqual("testString")
expect(defaultHelpers.camelCase("TestString")).toEqual("testString")
expect(defaultHelpers.camelCase("Test____String")).toEqual("testString")
})
test("pascalCase", () => {
expect(defaultHelpers.pascalCase("test string")).toEqual("TestString")
expect(defaultHelpers.pascalCase("test_string")).toEqual("TestString")
expect(defaultHelpers.pascalCase("test-string")).toEqual("TestString")
expect(defaultHelpers.pascalCase("testString")).toEqual("TestString")
expect(defaultHelpers.pascalCase("TestString")).toEqual("TestString")
expect(defaultHelpers.pascalCase("Test____String")).toEqual("TestString")
})
test("snakeCase", () => {
expect(defaultHelpers.snakeCase("test string")).toEqual("test_string")
expect(defaultHelpers.snakeCase("test_string")).toEqual("test_string")
expect(defaultHelpers.snakeCase("test-string")).toEqual("test_string")
expect(defaultHelpers.snakeCase("testString")).toEqual("test_string")
expect(defaultHelpers.snakeCase("TestString")).toEqual("test_string")
expect(defaultHelpers.snakeCase("Test____String")).toEqual("test_string")
})
test("kebabCase", () => {
expect(defaultHelpers.kebabCase("test string")).toEqual("test-string")
expect(defaultHelpers.kebabCase("test_string")).toEqual("test-string")
expect(defaultHelpers.kebabCase("test-string")).toEqual("test-string")
expect(defaultHelpers.kebabCase("testString")).toEqual("test-string")
expect(defaultHelpers.kebabCase("TestString")).toEqual("test-string")
expect(defaultHelpers.kebabCase("Test____String")).toEqual("test-string")
})
test("startCase", () => {
expect(defaultHelpers.startCase("test string")).toEqual("Test String")
expect(defaultHelpers.startCase("test_string")).toEqual("Test String")
expect(defaultHelpers.startCase("test-string")).toEqual("Test String")
expect(defaultHelpers.startCase("testString")).toEqual("Test String")
expect(defaultHelpers.startCase("TestString")).toEqual("Test String")
expect(defaultHelpers.startCase("Test____String")).toEqual("Test String")
})
})
describe("date helpers", () => {
describe("now", () => {
test("should work without extra params", () => {
const now = new Date()
const fmt = "yyyy-MM-dd HH:mm"
expect(nowHelper(fmt)).toEqual(dateFns.format(now, fmt))
})
})
describe("date", () => {
test("should work with no offset params", () => {
const now = new Date()
const fmt = "yyyy-MM-dd HH:mm"
expect(dateHelper(now.toISOString(), fmt)).toEqual(dateFns.format(now, fmt))
})
test("should work with offset params", () => {
const now = new Date()
const fmt = "yyyy-MM-dd HH:mm"
expect(dateHelper(now.toISOString(), fmt, -1, "days")).toEqual(
dateFns.format(dateFns.add(now, { days: -1 }), fmt),
)
expect(dateHelper(now.toISOString(), fmt, 1, "months")).toEqual(
dateFns.format(dateFns.add(now, { months: 1 }), fmt),
)
})
})
})
})
})

View File

@@ -3,7 +3,7 @@ import FileSystem from "mock-fs/lib/filesystem"
import Scaffold from "../src/scaffold"
import { readdirSync, readFileSync } from "fs"
import { Console } from "console"
import { defaultHelpers } from "../src/utils"
import { defaultHelpers } from "../src/parser"
import { join } from "path"
import * as dateFns from "date-fns"
import crypto from "crypto"
@@ -32,7 +32,7 @@ const fileStructWithData = {
const fileStructNested = {
input: {
"{{name}}-1.txt": "This should be in root",
"{{Name}}": {
"{{pascalCase name}}": {
"{{name}}-2.txt": "Hello, my value is {{value}}",
moreNesting: {
"{{name}}-3.txt": "Hi! My value is actually NOT {{value}}!",
@@ -99,7 +99,7 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
verbose: 0,
logLevel: "none",
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
@@ -110,8 +110,8 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
createSubFolder: true,
verbose: 0,
subdir: true,
logLevel: "none",
})
const data = readFileSync(join(process.cwd(), "output", "app_name", "app_name.txt"))
@@ -128,7 +128,7 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
verbose: 0,
logLevel: "none",
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
@@ -147,7 +147,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "1" },
verbose: 0,
logLevel: "none",
})
await Scaffold({
@@ -155,7 +155,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "2" },
verbose: 0,
logLevel: "none",
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
@@ -168,7 +168,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "1" },
verbose: 0,
logLevel: "none",
})
await Scaffold({
@@ -177,7 +177,7 @@ describe("Scaffold", () => {
templates: ["input"],
data: { value: "2" },
overwrite: true,
verbose: 0,
logLevel: "none",
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
@@ -205,7 +205,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["non-existing-input"],
data: { value: "1" },
verbose: 0,
logLevel: "none",
}),
).rejects.toThrow()
@@ -215,7 +215,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["non-existing-input/non-existing-file.txt"],
data: { value: "1" },
verbose: 0,
logLevel: "none",
}),
).rejects.toThrow()
@@ -242,7 +242,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "1" },
verbose: 0,
logLevel: "none",
dryRun: true,
})
@@ -257,10 +257,10 @@ describe("Scaffold", () => {
test("should allow override function", async () => {
await Scaffold({
name: "app_name",
output: (fullPath, basedir, basename) => join("custom-output", `${basename.split(".")[0]}`),
output: (_, __, basename) => join("custom-output", `${basename.split(".")[0]}`),
templates: ["input"],
data: { value: "1" },
verbose: 0,
logLevel: "none",
})
const data = readFileSync(join(process.cwd(), "/custom-output/app_name/app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
@@ -277,7 +277,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "1" },
verbose: 0,
logLevel: "none",
})
const rootDir = readdirSync(join(process.cwd(), "output"))
@@ -309,7 +309,7 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
verbose: 0,
logLevel: "none",
helpers: _helpers,
})
@@ -342,7 +342,7 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
verbose: 0,
logLevel: "none",
data: { customDate },
})
@@ -380,7 +380,7 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
verbose: 0,
logLevel: "none",
helpers: _helpers,
})
@@ -402,8 +402,8 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
createSubFolder: true,
verbose: 0,
subdir: true,
logLevel: "none",
})
const data = readFileSync(join(process.cwd(), "output", "app_name", "app_name.txt"))
@@ -415,9 +415,9 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
createSubFolder: true,
verbose: 0,
subFolderNameHelper: "upperCase",
subdir: true,
logLevel: "none",
subdirHelper: "upperCase",
})
const data = readFileSync(join(process.cwd(), "output", "APP_NAME", "app_name.txt"))
@@ -429,9 +429,9 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
createSubFolder: true,
verbose: 0,
subFolderNameHelper: "test",
subdir: true,
logLevel: "none",
subdirHelper: "test",
helpers: {
test: () => "REPLACED",
},
@@ -450,7 +450,7 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
verbose: 0,
logLevel: "none",
data: {
value: "value",
},
@@ -465,7 +465,7 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
verbose: 0,
logLevel: "none",
data: {
value: "value",
},
@@ -487,7 +487,7 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
verbose: 0,
logLevel: "none",
data: {
value: "value",
},

View File

@@ -1,124 +1,21 @@
import { dateHelper, handlebarsParse, nowHelper, parseAppendData } from "../src/utils"
import { ScaffoldCmdConfig, ScaffoldConfig } from "../src/types"
import path from "path"
import * as dateFns from "date-fns"
import { OptionsBase } from "massarg/types"
import { handleErr, resolve } from "../src/utils"
const blankConf: ScaffoldConfig = {
verbose: 0,
name: "",
output: "",
templates: [],
data: { name: "test" },
}
const blankCliConf: ScaffoldCmdConfig & OptionsBase = {
verbose: 0,
name: "",
output: "",
templates: [],
data: { name: "test" },
overwrite: false,
createSubFolder: false,
dryRun: false,
quiet: false,
extras: [],
help: false,
}
describe("Utils", () => {
describe("handlebarsParse", () => {
let origSep: any
describe("windows paths", () => {
beforeAll(() => {
origSep = path.sep
Object.defineProperty(path, "sep", { value: "\\" })
})
afterAll(() => {
Object.defineProperty(path, "sep", { value: origSep })
})
test("should work for windows paths", async () => {
expect(handlebarsParse(blankConf, "C:\\exports\\{{name}}.txt", { isPath: true })).toEqual(
Buffer.from("C:\\exports\\test.txt"),
)
})
describe("utils", () => {
describe("resolve", () => {
test("should resolve function", () => {
expect(resolve(() => 1, null)).toBe(1)
expect(resolve((x) => x, 2)).toBe(2)
})
describe("non-windows paths", () => {
beforeAll(() => {
origSep = path.sep
Object.defineProperty(path, "sep", { value: "/" })
})
afterAll(() => {
Object.defineProperty(path, "sep", { value: origSep })
})
test("should work for non-windows paths", async () => {
expect(handlebarsParse(blankConf, "/home/test/{{name}}.txt", { isPath: true })).toEqual(
Buffer.from("/home/test/test.txt"),
)
})
})
test("should not do path escaping on non-path compiles", async () => {
expect(
handlebarsParse(
{ ...blankConf, data: { ...blankConf.data, escaped: "value" } },
"/home/test/{{name}} \\{{escaped}}.txt",
{
isPath: false,
},
),
).toEqual(Buffer.from("/home/test/test {{escaped}}.txt"))
test("should resolve value", () => {
expect(resolve(1, null)).toBe(1)
expect(resolve(2, 1)).toBe(2)
})
})
describe("Helpers", () => {
describe("date helpers", () => {
describe("now", () => {
test("should work without extra params", () => {
const now = new Date()
const fmt = "yyyy-MM-dd HH:mm"
expect(nowHelper(fmt)).toEqual(dateFns.format(now, fmt))
})
})
describe("date", () => {
test("should work with no offset params", () => {
const now = new Date()
const fmt = "yyyy-MM-dd HH:mm"
expect(dateHelper(now.toISOString(), fmt)).toEqual(dateFns.format(now, fmt))
})
test("should work with offset params", () => {
const now = new Date()
const fmt = "yyyy-MM-dd HH:mm"
expect(dateHelper(now.toISOString(), fmt, -1, "days")).toEqual(
dateFns.format(dateFns.add(now, { days: -1 }), fmt),
)
expect(dateHelper(now.toISOString(), fmt, 1, "months")).toEqual(
dateFns.format(dateFns.add(now, { months: 1 }), fmt),
)
})
})
})
})
describe("parseAppendData", () => {
test('works for "key=value"', () => {
expect(parseAppendData("key=value", blankCliConf)).toEqual({ key: "value", name: "test" })
})
test('works for "key:=value"', () => {
expect(parseAppendData("key:=123", blankCliConf)).toEqual({ key: 123, name: "test" })
})
test("overwrites existing value", () => {
expect(parseAppendData("name:=123", blankCliConf)).toEqual({ name: 123 })
})
test("works with quotes", () => {
expect(parseAppendData('key="value test"', blankCliConf)).toEqual({ key: "value test", name: "test" })
describe("handleErr", () => {
test("should throw error", () => {
expect(() => handleErr({ name: "test", message: "test" })).toThrow()
expect(() => handleErr(null as never)).not.toThrow()
})
})
})

View File

@@ -1,16 +1,20 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2019",
"target": "ES2022",
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"lib": ["ES2019"],
"lib": ["ES2022"],
"declaration": true,
"outDir": "dist",
"strict": true,
"sourceMap": true,
"removeComments": false
"removeComments": false,
"paths": {
"@/*": ["./src/*"],
},
},
"include": ["src/index.ts", "src/cmd.ts"],
"exclude": ["tests/*"]
"exclude": ["tests/*"],
}

View File

@@ -1,4 +1,4 @@
const path = require("path")
const path = require("node:path")
/** @type {import('typedoc').TypeDocOptions} */
module.exports = {

6037
yarn.lock

File diff suppressed because it is too large Load Diff