Compare commits

..

15 Commits

Author SHA1 Message Date
Chen Asraf
414494734d bump version number 2021-11-28 14:24:01 +02:00
Chen Asraf
89b588f64e update workflows [skip publish] 2021-11-28 14:09:25 +02:00
Chen Asraf
cf22e2e62f fixes + add log level [skip publish] 2021-11-28 14:07:25 +02:00
Chen Asraf
246c139061 fix readme [skip publish] 2021-11-18 15:25:43 +02:00
Chen Asraf
711d8f0333 try fix release upload 2021-11-18 14:22:23 +02:00
Chen Asraf
8b22e96329 try new release version 2021-11-18 14:19:50 +02:00
Chen Asraf
53c0842ab8 fixed release tarball file location 2021-11-18 14:18:18 +02:00
Chen Asraf
48631325c1 fixed release tarball file location 2021-11-18 14:16:58 +02:00
Chen Asraf
045ad0118a fix build output files 2021-11-18 14:09:21 +02:00
Chen Asraf
b4dca7a954 add build step 2021-11-18 13:57:57 +02:00
Chen Asraf
7c42808f63 try to fix workflow 2021-11-18 13:56:24 +02:00
Chen Asraf
fd42013e8b publish: debug mode off, try to fix workflow 2021-11-18 13:55:30 +02:00
Chen Asraf
961a72fcdc publish: debug mode on 2021-11-18 13:51:29 +02:00
Chen Asraf
d6d99cfdf2 try fix workflow 2021-11-18 13:45:37 +02:00
Chen Asraf
ea4ecabe02 Merge pull request #12 from chenasraf/v1.0
v1.0
2021-11-18 13:44:09 +02:00
82 changed files with 3730 additions and 18692 deletions

View File

@@ -3,6 +3,3 @@ tab_width = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false

5
.github/FUNDING.yml vendored
View File

@@ -1,6 +1,6 @@
# These are supported funding model platforms
github: chenasraf
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: casraf
@@ -9,5 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom:
- "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TSH3C3ABGQM22&currency_code=ILS&source=url"
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -1,47 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG] "
labels: bug, needs-triage
assignees: chenasraf
---
#### Describe the bug
A clear and concise description of what the bug is.
#### How To Reproduce
Steps to reproduce the behavior:
1. Prepare templates:
```text
This is my {{ template }}
```
2. Run with args/config:
```shell
npx simple-scaffold@latest -t input -o output TplName
```
#### Expected behavior\*\*
A clear and concise description of what you expected to happen.
#### Logs
If applicable, paste your logs to help explain your problem. To see more logs, run the scaffold with
`-v 1` to enable debug logging.
#### Desktop (please complete the following information):
- OS: [e.g. macOS, Windows, Linux]
- OS Version: [e.g. Big Sur, 11, Ubuntu 20.04]
- Node.js: [e.g. 16.8]
- Simple Scaffold Version [e.g. 1.1.2]
#### Additional context
Add any other context about the problem here.

View File

@@ -1,24 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEATURE] "
labels: enhancement, needs-triage
assignees: chenasraf
---
#### Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
#### Describe the solution you'd like
A clear and concise description of what you want to happen.
#### Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered, if
applicable.
#### Additional context
Add any other context or screenshots about the feature request here.

50
.github/workflows/alpha.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Alpha Releases
on:
push:
branches: [alpha]
jobs:
build:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: "12.x"
- run: yarn install --frozen-lockfile
- run: yarn build
- run: cd ./dist && yarn pack --filename=../package.tgz
if: "!contains(github.event.head_commit.message, '[skip publish]')"
- uses: Klemensas/action-autotag@stable
if: "!contains(github.event.head_commit.message, '[skip publish]')"
id: update_tag
with:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
tag_prefix: "v"
- name: Publish on NPM
uses: JS-DevTools/npm-publish@v1
if: "!contains(github.event.head_commit.message, '[skip publish]')"
with:
package: ./dist/package.json
token: "${{ secrets.NPM_TOKEN }}"
- name: Create Release
if: steps.update_tag.outputs.tagname && !contains(github.event.head_commit.message, '[skip publish]')
uses: actions/create-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.update_tag.outputs.tagname }}
release_name: Release ${{ steps.update_tag.outputs.tagname }}
- name: Upload Release Asset
if: steps.update_tag.outputs.tagname && !contains(github.event.head_commit.message, '[skip publish]')
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./package.tgz
asset_name: package.tgz
asset_content_type: application/tgz

View File

@@ -1,33 +0,0 @@
name: Documentation
on:
push:
branches: [master, pre, develop]
jobs:
docs:
name: Build Documentation
runs-on: ubuntu-latest
# if: "contains(github.event.head_commit.message, 'chore(release)')"
steps:
- name: Checkout
uses: actions/checkout@v3
with:
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/build

49
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Releases
on:
push:
branches: [master]
jobs:
build:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: "12.x"
- run: yarn install --frozen-lockfile
- run: yarn build
- run: cd ./dist && yarn pack --filename=../package.tgz
if: "!contains(github.event.head_commit.message, '[skip publish]')"
- uses: Klemensas/action-autotag@stable
if: "!contains(github.event.head_commit.message, '[skip publish]')"
id: update_tag
with:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
tag_prefix: "v"
- name: Publish on NPM
uses: JS-DevTools/npm-publish@v1
with:
package: ./dist/package.json
token: "${{ secrets.NPM_TOKEN }}"
- name: Create Release
if: steps.update_tag.outputs.tagname && !contains(github.event.head_commit.message, '[skip publish]')
uses: actions/create-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.update_tag.outputs.tagname }}
release_name: Release ${{ steps.update_tag.outputs.tagname }}
- name: Upload Release Asset
if: steps.update_tag.outputs.tagname && !contains(github.event.head_commit.message, '[skip publish]')
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./package.tgz
asset_name: package.tgz
asset_content_type: application/tgz

View File

@@ -2,25 +2,16 @@ name: Pull Requests
on:
pull_request:
branches: [master, pre, develop]
branches: [master, alpha, beta]
jobs:
build:
name: Test & Build PR
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
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
node-version: "12.x"
- run: yarn install --frozen-lockfile
- run: yarn build
- run: yarn test

View File

@@ -1,38 +0,0 @@
name: Release
on:
push:
branches: [master, pre, develop]
permissions:
contents: read # for checkout
jobs:
release:
name: Release
runs-on: ubuntu-latest
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:
- name: Checkout
uses: actions/checkout@v3
with:
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
- name: Semantic Release
run: npx semantic-release
env:
NPM_TOKEN: "${{ secrets.NPM_TOKEN }}"
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

5
.gitignore vendored
View File

@@ -42,9 +42,6 @@ typings/
# Optional npm cache directory
.npm
# NPM
.npmrc
# Optional eslint cache
.eslintcache
@@ -64,5 +61,3 @@ examples/test-output/**/*
dist/
.DS_Store
tmp/
.nvmrc

View File

@@ -1,9 +0,0 @@
{
"line-length": {
"line_length": 100,
"tables": false,
"code_blocks": false
},
"no-inline-html": false,
"first-line-h1": false
}

View File

@@ -1,15 +1,5 @@
{
"semi": false,
"trailingComma": "all",
"printWidth": 120,
"tabWidth": 2,
"overrides": [
{
"files": "*.md",
"options": {
"printWidth": 100,
"proseWrap": "always"
}
}
]
"tabWidth": 2
}

50
.vscode/launch.json vendored
View File

@@ -1,27 +1,29 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug Scaffold",
"type": "node",
"request": "launch",
"protocol": "inspector",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/test.ts",
"outFiles": ["${workspaceRoot}/dist/test.js"],
"env": {
"NODE_ENV": "develop"
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug Scaffold",
"type": "node",
"request": "launch",
"protocol": "inspector",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/test.ts",
"outFiles": [
"${workspaceRoot}/dist/test.js"
],
"env": {
"NODE_ENV": "develop"
},
"sourceMaps": true,
},
"sourceMaps": true
},
{
"type": "node",
"request": "attach",
"name": "Attach by Process ID",
"processId": "${command:PickProcess}"
}
]
{
"type": "node",
"request": "attach",
"name": "Attach by Process ID",
"processId": "${command:PickProcess}"
}
]
}

16
.vscode/settings.json vendored
View File

@@ -2,18 +2,6 @@
"typescript.tsdk": "./node_modules/typescript/lib",
"npm.packageManager": "yarn",
"cSpell.words": [
"massarg",
"MYCOMPONENT",
"myname",
"nobrace",
"nocomment",
"nodir",
"noext",
"nonegate",
"subdir",
"variabletoken"
],
"[markdown]": {
"editor.rulers": [87, 100]
}
"massarg"
]
}

30
.vscode/tasks.json vendored
View File

@@ -5,55 +5,49 @@
"script": "build",
"label": "build",
"type": "npm",
"problemMatcher": []
"problemMatcher": [],
},
{
"script": "dev",
"label": "dev",
"type": "npm",
"problemMatcher": []
},
{
"command": "pnpm typedoc --watch",
"label": "typedoc --watch",
"type": "shell",
"problemMatcher": []
"problemMatcher": [],
},
{
"script": "start",
"label": "start",
"type": "npm",
"problemMatcher": []
"problemMatcher": [],
},
{
"script": "test",
"label": "test",
"type": "npm",
"problemMatcher": []
"problemMatcher": [],
},
{
"command": "pnpm test --watchAll",
"label": "pnpm test --watchAll",
"command": "yarn test --watchAll",
"label": "yarn test --watchAll",
"type": "shell",
"problemMatcher": []
"problemMatcher": [],
},
{
"script": "cmd",
"label": "cmd",
"type": "npm",
"problemMatcher": []
"problemMatcher": [],
},
{
"script": "build-test",
"label": "build-test",
"type": "npm",
"problemMatcher": []
"problemMatcher": [],
},
{
"script": "build-cmd",
"label": "build-cmd",
"type": "npm",
"problemMatcher": []
}
]
"problemMatcher": [],
},
],
}

View File

@@ -1,610 +0,0 @@
# Change Log
## [2.0.2](https://github.com/chenasraf/simple-scaffold/compare/v2.0.1...v2.0.2) (2024-02-04)
### Bug Fixes
* try to await scaffold before finally ([1b70897](https://github.com/chenasraf/simple-scaffold/commit/1b70897f9840e6365ff800490fbb813b9840177d))
## [2.0.1](https://github.com/chenasraf/simple-scaffold/compare/v2.0.0...v2.0.1) (2024-02-02)
### Bug Fixes
* log level flag ([5d7f449](https://github.com/chenasraf/simple-scaffold/commit/5d7f449050e50a6e4b2d00b7a2215cdb5fc9b611))
* rm tmp dir too early ([4aa52c8](https://github.com/chenasraf/simple-scaffold/commit/4aa52c84bd8cf302031e9f7f6407466aa736beb7))
# [2.0.0](https://github.com/chenasraf/simple-scaffold/compare/v1.9.0...v2.0.0) (2024-01-31)
* fix!: version number ([bc0a18d](https://github.com/chenasraf/simple-scaffold/commit/bc0a18dce01fefec6187192cb20c9303f7f7dbfa))
* remove gh flag ([939200c](https://github.com/chenasraf/simple-scaffold/commit/939200c9f21be240485ea602a73b983ba2f47aaf))
* tests ([ff92fd7](https://github.com/chenasraf/simple-scaffold/commit/ff92fd7607f1b86f36fc6b62652fdfc81cb391a3))
* try multiple default config files ([89aacb5](https://github.com/chenasraf/simple-scaffold/commit/89aacb58fd90a892f4994c758c61c43b2a6b1fba))
* 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))
* 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))
## 1.9.0 (2024-01-02)
* 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))
## 1.8.0 (2023-11-29)
* 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))
## <small>1.7.2 (2023-08-20)</small>
* chore(release): 1.7.2 [skip ci] ([d62eeeb](https://github.com/chenasraf/simple-scaffold/commit/d62eeeb))
## <small>1.7.2-pre.1 (2023-08-15)</small>
* 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))
## <small>1.7.1 (2023-06-07)</small>
* 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))
## <small>1.7.1-develop.1 (2023-06-07)</small>
* chore(release): 1.7.1-develop.1 [skip ci] ([77be7c0](https://github.com/chenasraf/simple-scaffold/commit/77be7c0))
## 1.7.0 (2023-05-17)
* chore(release): 1.7.0 [skip ci] ([4868925](https://github.com/chenasraf/simple-scaffold/commit/4868925))
## 1.7.0-develop.7 (2023-06-07)
* 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))
## 1.7.0-develop.6 (2023-05-27)
* 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))
## 1.7.0 (2023-05-17)
* chore(release): 1.7.0 [skip ci] ([4868925](https://github.com/chenasraf/simple-scaffold/commit/4868925))
## 1.7.0-develop.5 (2023-05-12)
* 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))
## 1.7.0-develop.4 (2023-05-11)
* 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))
## 1.7.0-develop.3 (2023-05-11)
* 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))
## 1.7.0-develop.2 (2023-05-10)
* 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))
## 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))

379
README.md
View File

@@ -1,216 +1,211 @@
<p align="center">
<img src="https://chenasraf.github.io//simple-scaffold/img/logo-lg.png" alt="Logo" />
</p>
# simple-scaffold
<h2 align="center">
Simple Scaffold allows you to create your structured files based on templates.
[GitHub](https://github.com/chenasraf/simple-scaffold) |
[Documentation](https://chenasraf.github.io/simple-scaffold) |
[NPM](https://npmjs.com/package/simple-scaffold) | [casraf.dev](https://casraf.dev)
## Install
![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)
You can either use it as a command line tool or import into your own code and run from there.
</h2>
Looking to streamline your workflow and get your projects up and running quickly? Look no further
than Simple Scaffold - the easy-to-use NPM package that simplifies the process of organizing and
copying your commonly-created files.
With its agnostic and un-opinionated approach, Simple Scaffold can handle anything from a few simple
files to an entire app boilerplate setup. Plus, with the power of **Handlebars.js** syntax, you can
easily replace custom data and personalize your files to fit your exact needs. But that's not all -
you can also use it to loop through data, use conditions, and write custom functions using helpers.
Don't waste any more time manually copying and pasting files - let Simple Scaffold do the heavy
lifting for you and start building your projects faster and more efficiently today!
<div align="center">
![Intro](https://chenasraf.github.io/simple-scaffold/img/intro.gif)
</div>
---
## Documentation
See full documentation [here](https://chenasraf.github.io/simple-scaffold).
- [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)
## Getting Started
### Cheat Sheet
A quick rundown of common usage scenarios:
- Remote template config file on GitHub:
```sh
npx simple-scaffold -g username/repository -c scaffold.js -k component NewComponentName
```
- Local template config file:
```sh
npx simple-scaffold -c scaffold.js -k component NewComponentName
```
- Local one-time usage:
```sh
npx simple-scaffold -t templates/component -o src/components NewComponentName
```
### Remote Configurations
The fastest way to get started is to 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:
- For templates hosted on GitHub, the syntax is `-g user/repository_name`
- For other Git platforms like GitLab, use `-g https://example.com/user/repository_name.git`
These remote configurations support multiple scaffold groups, which can be specified using the
`--key` or `-k` argument:
```sh
$ npx simple-scaffold \
-g chenasraf/simple-scaffold \
-k component \
PageWrapper
# equivalent to:
$ npx simple-scaffold \
-g https://github.com/chenasraf/simple-scaffold.git \
-c scaffold.config.js \
-k component \
PageWrapper
```bash
# npm
npm install [-g] simple-scaffold
# yarn
yarn [global] add simple-scaffold
# run without installing
npx simple-scaffold <...args>
```
By default, the template name is set to `default` when the `--key` option is not provided.
## Use as a command line tool
See information about each option and flag using the `--help` flag, or read the
[CLI documentation](https://chenasraf.github.io/simple-scaffold/docs/usage/cli). For information
about how configuration files work, [see below](#configuration-files).
### Command Line Options
### Configuration Files
```plaintext
Usage: simple-scaffold [options]
You can use a config file to more easily maintain all your scaffold definitions.
Options:
`scaffold.config.js`
--help|-h Display help information
--name|-n Name to be passed to the generated files. {{name}} and
{{Name}} inside contents and file names will be replaced
accordingly.
--output|-o Path to output to. If --create-sub-folder is enabled, the
subfolder will be created inside this path.
--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.
--create-sub-folder|-s Create subfolder with the input name (default: false)
--quiet|-q Suppress output logs (Same as --verbose 0) (default: false)
--verbose|-v Determine amount of logs to display. The values are: 0 (none)
| 1 (debug) | 2 (info) | 3 (warn) | 4 (error). The provided level
will display messages of the same level or higher.
(default: 2)
--dry-run|-dr Don't emit files. This is good for testing your scaffolds and
making sure they don't fail, without having to write actual file
contents or create directories. (default: false)
```
You can also add this as a script in your `package.json`:
```json
{
"scripts": {
"scaffold": "yarn simple-scaffold --templates scaffolds/component/**/* --output src/components --data '{\"myProp\": \"propName\", \"myVal\": \"123\"}'"
}
}
```
## Use in Node.js
You can also build the scaffold yourself, if you want to create more complex arguments or scaffold groups.
Simply pass a config object to the constructor, and invoke `run()` when you are ready to start.
The config takes similar arguments to the command line:
```javascript
const SimpleScaffold = require("simple-scaffold").default
const scaffold = SimpleScaffold({
name: "component",
templates: [path.join(__dirname, "scaffolds", "component")],
output: path.join(__dirname, "src", "components"),
createSubFolder: true,
locals: {
property: "value",
},
})
```
The exception in the config is that `output`, when used in Node directly, may also be passed a
function for each input file to output into a dynamic path:
```javascript
config.output = (fullPath, baseDir, baseName) => {
console.log({ fullPath, baseDir, baseName })
return [baseDir, baseName].join(path.sep)
}
```
## Preparing files
### Template files
Put your template files anywhere, and fill them with tokens for replacement.
### Variable/token replacement
Scaffolding will replace `{{ varName }}` in both the file name and its contents and put the
transformed files in the output directory.
The data available for the template parser is the data you pass to the `data` config option (or
`--data` argument in CLI).
Your `data` will be pre-populated with the following:
- `{{Name}}`: PascalCase of the component name
- `{{name}}`: raw name of the component
> Simple-Scaffold uses [Handlebars.js](https://handlebarsjs.com/) for outputting the file contents,
> see their documentation for more information on syntax.
> Any `data` you add in the config will be available for use with their names wrapped in
> `{{` and `}}`.
Simple-Scaffold provides some built-in text transformation filters usable by handleBars.
For example, you may use `{{ snakeCase name }}` inside a template file or filename, and it will
replace `My Name` with `my_name` when producing the final value.
Here are the built-in helpers available for use:
| Helper name | Example code | Example output |
| ----------- | ----------------------- | -------------- |
| camelCase | `{{ camelCase name }}` | myName |
| snakeCase | `{{ snakeCase name }}` | my_name |
| startCase | `{{ startCase name }}` | My Name |
| kebabCase | `{{ kebabCase name }}` | my-name |
| hyphenCase | `{{ hyphenCase name }}` | my-name |
| pascalCase | `{{ pascalCase name }}` | MyName |
**Note:** These helpers are available for any data property, not exclusive to `name`.
## Examples
### Command Example
```bash
simple-scaffold MyComponent \
-t project/scaffold/**/* \
-o src/components \
-d '{"className":"myClassName"}'
```
### Example Scaffold Input
#### Input Directory structure
```plaintext
- project
- scaffold
- {{Name}}.js
- src
- components
- ...
```
#### Contents of `project/scaffold/{{Name}}.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: {
// ...
},
},
const React = require('react')
module.exports = class {{Name}} extends React.Component {
render() {
<div className="{{className}}">{{Name}} Component</div>
}
}
```
Then call your scaffold like this:
### Example Scaffold Output
```sh
$ npx simple-scaffold -c scaffold.config.js PageWrapper
### Output directory structure
```plaintext
- project
- src
- components
- MyComponent
- MyComponent.js
- ...
```
This will allow you to avoid needing to remember which configs are needed or to store them in a
one-liner in `package.json` which can get pretty long and messy, and harder to maintain.
With `createSubfolder = false`:
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)
```plaintext
- project
- src
- components
- MyComponent.js
- ...
```
More information can be found at the
[Configuration Files documentation](https://chenasraf.github.io/simple-scaffold/docs/usage/configuration_files).
#### Contents of `project/scaffold/MyComponent/MyComponent.js`
### Templates Structure
```js
const React = require("react")
Templates are **any file** in the a directory given to `--templates`.
Simple Scaffold will maintain any file and directory structure you try to generate, while replacing
any tokens such as `{{ name }}` or other custom-data using
[Handlebars.js](https://handlebarsjs.com/).
`templates/component/{{ pascalName name }}.tsx`
```tsx
// Created: {{ now 'yyyy-MM-dd' }}
import React from 'react'
export default {{pascalCase name}}: React.FC = (props) => {
return (
<div className="{{camelCase name}}">{{pascalCase name}} Component</div>
)
module.exports = class MyComponent extends React.Component {
render() {
<div className="myClassName">MyComponent Component</div>
}
}
```
To generate the template output once without saving a configuration file, run:
```sh
# generate single component
$ npx simple-scaffold \
-t templates/component \
-o src/components \
PageWrapper
```
This will immediately create the following file: `src/components/PageWrapper.tsx`
```tsx
// Created: 2077-01-01
import React from 'react'
export default PageWrapper: React.FC = (props) => {
return (
<div className="pageWrapper">PageWrapper Component</div>
)
}
```
## Contributing
I am developing this package on my free time, so any support, whether code, issues, or just stars is
very helpful to sustaining its life. If you are feeling incredibly generous and would like to donate
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'
src='https://cdn.ko-fi.com/cdn/kofi1.png?v=3'
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,
don't hesitate to open an appropriate issue and I will do my best to reply promptly.
If you are a developer and want to contribute code, here are some starting tips:
1. Fork this repository
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)
7. Create a PR on upstream
Some tips on getting around the code:
- 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.
- Use `pnpm test` to run tests
- Use `pnpm docs:build` to build the documentation once
- Use `pnpm docs:watch` to start docs in watch mode
- Use `pnpm build` to build the output

3
babel.config.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
plugins: ["@babel/plugin-proposal-nullish-coalescing-operator"],
}

22
docs/.gitignore vendored
View File

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

View File

@@ -1,44 +0,0 @@
# 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.

View File

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

View File

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

View File

@@ -1,90 +0,0 @@
---
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 |
| `--version` \| `-v` | Display version. |
## 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

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

View File

@@ -1,141 +0,0 @@
---
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",
},
},
})
```

View File

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

View File

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

View File

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

View File

@@ -1,178 +0,0 @@
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.svg",
// 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/chenasraf/simple-scaffold/blob/master/docs",
},
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/chenasraf/simple-scaffold",
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

View File

@@ -1,51 +0,0 @@
{
"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

File diff suppressed because it is too large Load Diff

View File

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

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

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

View File

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

@@ -1,34 +0,0 @@
/**
* 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;
}
.heroImage {
margin-bottom: 1.5rem;
}
.logo {
width: 100%;
max-width: 300px;
margin: 0 auto;
}

View File

@@ -1,44 +0,0 @@
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">
<img className={styles.logo} src="img/logo-lg.svg" alt="Simple Scaffold" />
<Heading as="h1" className="hero__title">
{siteConfig.title}
</Heading>
<p className="hero__subtitle">{siteConfig.tagline}</p>
<img className={styles.heroImage} src="img/intro.gif" alt="Simple-Scaffold doing its thing" />
<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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 432 KiB

View File

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

Before

Width:  |  Height:  |  Size: 31 KiB

View File

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

Before

Width:  |  Height:  |  Size: 35 KiB

View File

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

Before

Width:  |  Height:  |  Size: 12 KiB

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

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

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

@@ -13,7 +13,7 @@ export default {
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/private/var/folders/q9/0mns8fgd00b4t5j5lq2wh2yh0000gn/T/jest_dx",
// Automatically clear mock calls, instances, contexts and results before every test
// Automatically clear mock calls and instances between every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
@@ -26,7 +26,9 @@ export default {
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
coveragePathIgnorePatterns: ["/node_modules/", "scaffold.config.js"],
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
@@ -48,11 +50,6 @@ export default {
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// The default configuration for fake timers
// fakeTimers: {
// "enableGlobally": false
// },
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
@@ -76,8 +73,6 @@ export default {
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "mjs",
// "cjs",
// "jsx",
// "ts",
// "tsx",
@@ -87,11 +82,6 @@ export default {
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// moduleNameMapper: {
// chalk: "<rootDir>/node_modules/chalk/source/index.js",
// "#ansi-styles": "<rootDir>/node_modules/chalk/source/vendor/ansi-styles/index.js",
// "#supports-color": "<rootDir>/node_modules/chalk/source/vendor/supports-color/index.js",
// },
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
modulePathIgnorePatterns: ["<rootDir>/dist"],
@@ -111,7 +101,7 @@ export default {
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state before every test
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
@@ -120,7 +110,7 @@ export default {
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state and implementation before every test
// Automatically restore mock state between every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
@@ -147,7 +137,7 @@ export default {
// snapshotSerializers: [],
// The test environment that will be used for testing
// testEnvironment: "jest-environment-node",
testEnvironment: "node",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
@@ -175,8 +165,14 @@ export default {
// This option allows use of a custom test runner
// testRunner: "jest-circus/runner",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
// transform: undefined,
// transform: {},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
@@ -184,20 +180,17 @@ export default {
// "\\.pnp\\.[^\\/]+$"
// ],
// transform: {
// "^.+\\.ts?$": "ts-jest",
// },
// transformIgnorePatterns: ["<rootDir>/node_modules/"],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
verbose: true,
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
// extensionsToTreatAsEsm: [".ts"],
}

View File

@@ -1,6 +0,0 @@
{
"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,66 +1,42 @@
{
"name": "simple-scaffold",
"version": "2.0.2",
"description": "Generate any file structure - from single components to entire app boilerplates, with a single command.",
"homepage": "https: //chenasraf.github.io/simple-scaffold",
"repository": {
"type": "git",
"url": "https://github.com/chenasraf/simple-scaffold.git"
},
"author": "Chen Asraf <contact@casraf.dev> (https://casraf.dev)",
"version": "1.0.0-alpha.6",
"description": "Create files based on templates",
"repository": "https://github.com/chenasraf/simple-scaffold.git",
"author": "Chen Asraf <inbox@casraf.com>",
"license": "MIT",
"main": "index.js",
"bin": {
"simple-scaffold": "cmd.js"
},
"packageManager": "pnpm@8.15.1",
"keywords": [
"javascript",
"cli",
"template",
"files",
"typescript",
"generator",
"scaffold",
"file",
"scaffolding"
],
"bin": "cmd.js",
"types": "types.d.ts",
"scripts": {
"clean": "rm -rf dist/",
"build": "pnpm clean && tsc && chmod -R +x ./dist && cp ./package.json ./README.md ./dist/",
"build": "yarn clean && tsc && chmod -R +x ./dist && cp ./package.json ./dist/ && cp ./README.md ./dist/",
"dev": "tsc --watch",
"start": "ts-node src/scaffold.ts",
"test": "jest",
"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"
"start": "node dist/scaffold.js",
"test": "jest --verbose",
"cmd": "node --trace-warnings dist/cmd.js",
"build-test": "yarn build && yarn test",
"build-cmd": "yarn build && yarn cmd"
},
"dependencies": {
"chalk": "^4.1.2",
"date-fns": "^3.3.1",
"glob": "^10.3.10",
"handlebars": "^4.7.8",
"massarg": "2.0.0"
"args": "^5.0.1",
"glob": "^7.1.3",
"handlebars": "^4.7.7",
"lodash": "^4.17.21",
"massarg": "^0.1.2",
"util.promisify": "^1.1.1"
},
"devDependencies": {
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@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",
"semantic-release": "^23.0.0",
"semantic-release-conventional-commits": "^3.0.0",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
"@types/args": "^3.0.1",
"@types/glob": "^7.1.1",
"@types/jest": "^26.0.24",
"@types/lodash": "^4.14.171",
"@types/mock-fs": "^4.13.1",
"@types/node": "^14.14.22",
"jest": "^27.0.6",
"mock-fs": "^5.0.0",
"ts-jest": "^27.0.3",
"ts-node": "^10.1.0",
"typescript": "^4.3.5"
}
}

4576
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,69 +0,0 @@
const ref = process.env.GITHUB_REF || ""
const branch = ref.split("/").pop()
/**
* @type {import('semantic-release').GlobalConfig}
*/
module.exports = {
branches: ["master", { name: "pre", prerelease: true }],
plugins: [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/npm",
{
// 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/exec",
{
publish: "cd ./dist && pnpm pack --pack-destination=../",
},
],
[
"@semantic-release/npm",
{
// publish from dist dir instead of root
pkgRoot: "dist",
},
],
[
"@semantic-release/github",
{
assets: ["*.tgz"],
},
],
branch === "master"
? [
"@semantic-release/changelog",
{
changelogFile: "CHANGELOG.md",
changelogTitle: "# Change Log",
},
]
: undefined,
[
"@semantic-release/git",
{
assets: ["package.json", "CHANGELOG.md"].filter(Boolean),
},
],
//
// [
// '@semantic-release/exec',
// {
// verifyReleaseCmd: 'echo ${nextRelease.version} > .VERSION',
// },
// ],
].filter(Boolean),
}

View File

@@ -1,16 +0,0 @@
/** @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,200 +1,84 @@
#!/usr/bin/env node
import Scaffold from "./scaffold"
import massarg from "massarg"
import { ScaffoldCmdConfig } from "./types"
import { LogLevel } from "."
import os from "node:os"
import { massarg } from "massarg"
import chalk from "chalk"
import { LogLevel, ScaffoldCmdConfig } from "./types"
import { Scaffold } from "./scaffold"
import path from "node:path"
import fs from "node:fs/promises"
import { parseAppendData, parseConfigFile } from "./config"
import { log } from "./logger"
export async function parseCliArgs(args = process.argv.slice(2)) {
const isProjectRoot = Boolean(await fs.stat(path.join(__dirname, "package.json")).catch(() => false))
const pkgFile = await fs.readFile(path.resolve(__dirname, isProjectRoot ? "." : "..", "package.json"))
const pkg = JSON.parse(pkgFile.toString())
const isVersionFlag = args.includes("--version") || args.includes("-v")
const isConfigProvided =
args.includes("--config") || args.includes("-c") || args.includes("--git") || args.includes("-g") || isVersionFlag
return massarg<ScaffoldCmdConfig>({
name: pkg.name,
description: pkg.description,
massarg<ScaffoldCmdConfig & { help: boolean; extras: string[] }>()
.main(Scaffold)
.option({
name: "name",
aliases: ["n"],
isDefault: true,
description:
"Name to be passed to the generated files. {{name}} and {{Name}} inside contents and file names will be replaced accordingly.",
})
.main(async (config) => {
if (config.version) {
console.log(pkg.version)
return
}
log(config, LogLevel.info, `Simple Scaffold v${pkg.version}`)
const tmpPath = path.resolve(os.tmpdir(), `scaffold-config-${Date.now()}`)
try {
log(config, LogLevel.debug, "Parsing config file...", config)
const parsed = await parseConfigFile(config, tmpPath)
await Scaffold(parsed)
} finally {
log(config, LogLevel.debug, "Cleaning up temporary files...", tmpPath)
await fs.rm(tmpPath, { recursive: true, force: true })
}
})
.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: !isVersionFlag,
})
.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: (v) => {
const val = v.toLowerCase()
if (!(val in LogLevel)) {
throw new Error(`Invalid log level: ${val}, must be one of: ${Object.keys(LogLevel).join(", ")}`)
}
return val
},
})
.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.",
})
.flag({
name: "version",
aliases: ["v"],
description: "Display version.",
})
.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()
.option({
name: "output",
aliases: ["o"],
description:
"Path to output to. If --create-sub-folder is enabled, the subfolder will be created inside this path.",
})
.option({
name: "templates",
aliases: ["t"],
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.",
defaultValue: [],
array: true,
})
.option({
aliases: ["w"],
name: "overwrite",
description: "Enable to override output files, even if they already exist.",
defaultValue: false,
boolean: true,
})
.option({
aliases: ["d"],
name: "data",
description: "Add custom data to the templates. By default, only your app name is included.",
parse: (v) => JSON.parse(v),
})
.option({
aliases: ["s"],
name: "create-sub-folder",
description: "Create subfolder with the input name",
defaultValue: false,
boolean: true,
})
.option({
aliases: ["q"],
name: "quiet",
description: "Suppress output logs (Same as --verbose 0)",
defaultValue: false,
boolean: true,
})
.option({
aliases: ["v"],
name: "verbose",
description:
"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.",
defaultValue: LogLevel.Info,
parse: Number,
})
.option({
aliases: ["dr"],
name: "dry-run",
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.",
defaultValue: false,
boolean: true,
})
// .example({
// input: `yarn cmd -t examples/test-input/Component -o examples/test-output -d '{"property":"myProp","value":"10"}'`,
// description: "Usage",
// output: "",
// })
.help({
binName: "simple-scaffold",
useGlobalColumns: true,
usageExample: "[options]",
})
.parse()

View File

@@ -1,140 +0,0 @@
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, tmpPath: string): 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 (isGit
? getRemoteConfig({ git: configPath, config: configFilename, logLevel: config.logLevel, tmpPath })
: getLocalConfig({ config: configFilename, logLevel: config.logLevel }))
// resolve the config
let configImport = await resolve(configPromise, config)
// If the config is a function or promise, return the output
if (typeof configImport.default === "function" || configImport.default instanceof Promise) {
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, tmpPath, ...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, tmpPath, logConfig)
}

View File

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

View File

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

View File

@@ -1,81 +0,0 @@
import path from "node:path"
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,
tmpPath: string,
logConfig: LogConfig,
): Promise<AsyncResolver<ScaffoldCmdConfig, ScaffoldConfigMap>> {
const repoUrl = `${url.protocol}//${url.host}${url.pathname}`
log(logConfig, LogLevel.info, `Cloning git repo ${repoUrl}`)
return new Promise((res, reject) => {
log(logConfig, LogLevel.debug, `Cloning git repo to ${tmpPath}`)
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)
log(logConfig, LogLevel.debug, `Resolving config file: ${absolutePath}`)
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)),
}
}
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`)
}

View File

@@ -1,5 +1,4 @@
export * from "./scaffold"
export * from "./types"
import Scaffold from "./scaffold"
export default Scaffold

View File

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

View File

@@ -1,127 +0,0 @@
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.info, "Couldn't parse file with handlebars, returning original content")
return Buffer.from(templateBuffer)
}
}

View File

@@ -1,140 +1,137 @@
/**
* @module
* Simple Scaffold
*
* See [readme](README.md)
*/
import path from "node:path"
import os from "node:os"
import { glob } from "glob"
import path from "path"
import { promisify } from "util"
import { promises as fsPromises } from "fs"
const { readFile, writeFile } = fsPromises
import { handleErr, resolve } from "./utils"
import {
createDirIfNotExists,
getOptionValueForFile,
handleErr,
handlebarsParse,
log,
pathExists,
pascalCase,
isDir,
removeGlob,
makeRelativePath,
getTemplateGlobInfo,
getFileList,
getBasePath,
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"
} from "./utils"
import { LogLevel, ScaffoldConfig } from "./types"
/**
* Create a scaffold using given `options`.
*
* #### Create files
* To create a file structure to output, use any directory and file structure you would like.
* Inside folder names, file names or file contents, you may place `{{ var }}` where `var` is either
* `name` which is the scaffold name you provided or one of the keys you provided in the `data` option.
*
* The contents and names will be replaced with the transformed values so you can use your original structure as a
* boilerplate for other projects, components, modules, or even single files.
*
* The files will maintain their structure, starting from the directory containing the template (or the template itself
* if it is already a directory), and will output from that directory into the directory defined by `config.output`.
*
* #### Helpers
* Helpers are functions you can use to transform your `{{ var }}` contents into other values without having to
* pre-define the data and use a duplicated key.
*
* Any functions you provide in `helpers` option will also be available to you to make custom formatting as you see fit
* (for example, formatting a date)
*
* For available default values, see {@link DefaultHelpers}.
*
* @param {ScaffoldConfig} config The main configuration object
* @return {Promise<void>} A promise that resolves when the scaffold is complete
*
* @see {@link DefaultHelpers}
* @see {@link CaseHelpers}
* @see {@link DateHelpers}
*
* @category Main
*/
export async function Scaffold(config: ScaffoldConfig): Promise<void> {
config.output ??= process.cwd()
registerHelpers(config)
export async function Scaffold(config: ScaffoldConfig) {
try {
config.data = { name: config.name, ...config.data }
logInitStep(config)
for (let _template of config.templates) {
const options = { ...config }
const data = { name: options.name, Name: pascalCase(options.name), ...options.data }
log(options, LogLevel.Debug, "Full config:", {
name: options.name,
templates: options.templates,
output: options.output,
createSubfolder: options.createSubFolder,
data: options.data,
overwrite: options.overwrite,
quiet: options.quiet,
})
log(options, LogLevel.Info, "Data:", data)
for (let template of config.templates) {
try {
const { nonGlobTemplate, origTemplate, isDirOrGlob, isGlob, template } = await getTemplateGlobInfo(
config,
_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
const _isGlob = template.includes("*")
const _nonGlobTemplate = _isGlob ? removeGlob(template) : template
const _isDir = _isGlob ? false : await isDir(template)
const _shouldAddGlob = !_isGlob && !_isDir
if (_shouldAddGlob) {
template = template + "/**/*"
}
const files = await promisify(glob)(template, { dot: true, debug: false })
for (const templatePath of files) {
if (!(await isDir(templatePath))) {
const basePath = path
.resolve(
process.cwd(),
_isDir
? templatePath.replace(template, "")
: path.dirname(removeGlob(templatePath).replace(_nonGlobTemplate, ""))
)
.replace(process.cwd() + "/", "")
.replace(process.cwd(), "")
log(
options,
LogLevel.Debug,
`\ntemplate: ${template}\ntemplatePath: ${templatePath}, \nbase path: ${basePath}\n`
)
await handleTemplateFile(templatePath, basePath, options, data)
}
const relPath = makeRelativePath(path.dirname(removeGlob(inputFilePath).replace(nonGlobTemplate, "")))
const basePath = getBasePath(relPath)
logInputFile(config, {
originalTemplate: origTemplate,
relativePath: relPath,
parsedTemplate: template,
inputFilePath,
nonGlobTemplate,
basePath,
isDirOrGlob,
isGlob,
})
await handleTemplateFile(config, {
templatePath: inputFilePath,
basePath,
})
}
} catch (e: any) {
handleErr(e)
}
}
} catch (e: any) {
log(config, LogLevel.error, e)
console.error(e)
throw e
}
}
/**
* Create a scaffold based on a config file or URL.
*
* @param {string} pathOrUrl The path or URL to the config file
* @param {Record<string, string>} config Information needed before loading the config
* @param {Partial<Omit<ScaffoldConfig, 'name'>>} overrides Any overrides to the loaded config
*
* @see {@link Scaffold}
* @category Main
* @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,
/** Information needed before loading the config */
config: MinimalConfig,
/** Any overrides to the loaded config */
overrides?: Resolver<ScaffoldCmdConfig, Partial<Omit<ScaffoldConfig, "name">>>,
async function handleTemplateFile(
templatePath: string,
basePath: string,
options: ScaffoldConfig,
data: Record<string, string>
): Promise<void> {
const _cmdConfig: ScaffoldCmdConfig = {
dryRun: false,
output: process.cwd(),
logLevel: LogLevel.info,
overwrite: false,
templates: [],
subdir: false,
quiet: false,
config: pathOrUrl,
version: false,
...config,
}
const tmpPath = path.resolve(os.tmpdir(), `scaffold-config-${Date.now()}`)
const _overrides = resolve(overrides, _cmdConfig)
const _config = await parseConfigFile(_cmdConfig, tmpPath)
return Scaffold({ ..._config, ..._overrides })
return new Promise(async (resolve, reject) => {
try {
const inputPath = path.resolve(process.cwd(), templatePath)
const outputPathOpt = getOptionValueForFile(inputPath, data, options.output)
const outputDir = path.resolve(
process.cwd(),
...([outputPathOpt, basePath, options.createSubFolder ? options.name : undefined].filter(Boolean) as string[])
)
log(
options,
LogLevel.Debug,
`\nParsing ${templatePath}`,
`\nBase path: ${basePath}`,
`\nFull input path: ${inputPath}`,
`\nFull output path: ${outputDir}\n`
)
const outputPath = path.join(outputDir, handlebarsParse(path.basename(inputPath), data))
const overwrite = getOptionValueForFile(inputPath, data, options.overwrite ?? false)
const exists = await pathExists(outputPath)
log(
options,
LogLevel.Debug,
"Filename parsed:",
handlebarsParse(path.basename(inputPath), data),
"Orig:",
path.basename(inputPath)
// "Test:",
// handlebarsParse("{{name}} {{name pascalCase}}", data)
)
await createDirIfNotExists(outputDir, options)
log(options, LogLevel.Info, `Writing to ${outputPath}`)
if (!exists || overwrite) {
if (exists && overwrite) {
log(options, LogLevel.Info, `File ${outputPath} exists, overwriting`)
}
const templateBuffer = await readFile(inputPath)
const outputContents = handlebarsParse(templateBuffer, data)
if (!options.dryRun) {
await writeFile(outputPath, outputContents)
} else {
log(options, LogLevel.Info, "Content output:")
log(options, LogLevel.Info, outputContents)
}
} else if (exists) {
log(options, LogLevel.Info, `File ${outputPath} already exists, skipping`)
}
resolve()
} catch (e: any) {
handleErr(e)
reject(e)
}
})
}
export default Scaffold

View File

@@ -1,415 +1,36 @@
import { HelperDelegate } from "handlebars/runtime"
export enum LogLevel {
None = 0,
Debug = 1,
Info = 2,
Warning = 3,
Error = 4,
}
export type FileResponseFn<T> = (fullPath: string, basedir: string, basename: string) => T
export type FileResponse<T> = T | FileResponseFn<T>
/**
* The config object for defining a scaffolding group.
*
* @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}
*
* @category Config
*/
export interface ScaffoldConfig {
/**
* Name to be passed to the generated files. `{{name}}` and `{{Name}}` inside contents and file names will be replaced
* accordingly.
*/
/** The name supplied for the output templates */
name: string
/**
* 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.
*
* @default Current working directory
*/
/** Template input files/dirs/glob patterns to use as template input. These will be copied to the output directory. */
templates: string[]
/**
* 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.
*
* @see {@link FileResponse}
* @see {@link FileResponseHandler}
*/
/** Output directory to put scaffolded files in. */
output: FileResponse<string>
/**
* Whether to create subfolder with the input name.
*
* When `true`, you may also use {@link subdirHelper} to determine a pre-process helper on
* the directory name.
*
* @default `false`
*/
subdir?: boolean
/**
* Add custom data to the templates. By default, only your app name is included as `{{name}}` and `{{Name}}`.
*
* This can be any object that will be usable by Handlebars.
*/
data?: Record<string, any>
/**
* 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 boolean for each file.
*
* May also be a {@link FileResponseHandler} which returns a boolean value per file.
*
* @see {@link FileResponse}
* @see {@link FileResponseHandler}
*
* @default `false`
*/
overwrite?: FileResponse<boolean>
/**
* 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.
*
* @see {@link LogLevel}
*
* @default `2 (info)`
*/
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.
*
* @default `false`
*/
dryRun?: boolean
/**
* Additional helpers to add to the template parser. Provide an object whose keys are the name of the function to add,
* and the value is the helper function itself. The signature of helpers is as follows:
* ```typescript
* (text: string, ...args: any[]) => string
* ```
*
* A full example might be:
*
* ```typescript
* Scaffold({
* //...
* helpers: {
* upperKebabCase: (text) => kebabCase(text).toUpperCase()
* }
* })
* ```
*
* Which will allow:
*
* ```
* {{ upperKebabCase "my value" }}
* ```
*
* To transform to:
*
* ```
* MY-VALUE
* ```
*
* See {@link DefaultHelpers} for a list of all the built-in available helpers.
*
* Simple Scaffold uses Handlebars.js, so all the syntax from there is supported. See
* [their docs](https://handlebarsjs.com/guide/#custom-helpers) for more information.
*
* @see {@link DefaultHelpers}
* @see {@link CaseHelpers}
* @see {@link DateHelpers}
* @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 `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 subdir}
* @see {@link CaseHelpers}
* @see {@link DefaultHelpers}
*/
subdirHelper?: DefaultHelpers | string
/**
* This callback runs right before content is being written to the disk. If you supply this function, you may return
* a string that represents the final content of your file, you may process the content as you see fit. For example,
* you may run formatters on a file, fix output in edge-cases not supported by helpers or data, etc.
*
* If the return value of this function is `undefined`, the original content will be used.
*
* @param content The original template after token replacement
* @param rawContent The original template before token replacement
* @param outputPath The final output path of the processed file
*
* @returns {Promise<String | Buffer | undefined> | String | Buffer | undefined} The final output of the file
* contents-only, after further modifications - or `undefined` to use the original content (i.e. `content.toString()`)
*/
beforeWrite?(
content: Buffer,
rawContent: Buffer,
outputPath: string,
): string | Buffer | undefined | Promise<string | Buffer | undefined>
}
/**
* The names of the available helper functions that relate to text capitalization.
*
* These are available for `subfolderNameHelper`.
*
* | Helper name | Example code | Example output |
* | ------------ | ----------------------- | -------------- |
* | [None] | `{{ name }}` | my name |
* | `camelCase` | `{{ camelCase name }}` | myName |
* | `snakeCase` | `{{ snakeCase name }}` | my_name |
* | `startCase` | `{{ startCase name }}` | My Name |
* | `kebabCase` | `{{ kebabCase name }}` | my-name |
* | `hyphenCase` | `{{ hyphenCase name }}` | my-name |
* | `pascalCase` | `{{ pascalCase name }}` | MyName |
* | `upperCase` | `{{ upperCase name }}` | MY NAME |
* | `lowerCase` | `{{ lowerCase name }}` | my name |
*
* @see {@link DefaultHelpers}
* @see {@link DateHelpers}
* @see {@link ScaffoldConfig}
* @see {@link ScaffoldConfig.subdirHelper}
*
* @category Helpers
*/
export type CaseHelpers =
| "camelCase"
| "hyphenCase"
| "kebabCase"
| "lowerCase"
| "pascalCase"
| "snakeCase"
| "startCase"
| "upperCase"
/**
* The names of the available helper functions that relate to dates.
*
* | Helper name | Description | Example code | Example output |
* | -------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------- | ------------------ |
* | `now` | Current date with format | `{{ now "yyyy-MM-dd HH:mm" }}` | `2042-01-01 15:00` |
* | `now` (with offset) | Current date with format, and with offset | `{{ now "yyyy-MM-dd HH:mm" -1 "hours" }}` | `2042-01-01 14:00` |
* | `date` | Custom date with format | `{{ date "2042-01-01T15:00:00Z" "yyyy-MM-dd HH:mm" }}` | `2042-01-01 15:00` |
* | `date` (with offset) | Custom date with format, and with offset | `{{ date "2042-01-01T15:00:00Z" "yyyy-MM-dd HH:mm" -1 "days" }}` | `2041-31-12 15:00` |
* | `date` (with date from `--data`) | Custom date with format, with data from the `data` config option | `{{ date myCustomDate "yyyy-MM-dd HH:mm" }}` | `2042-01-01 12:00` |
*
* Further details:
*
* - We use [`date-fns`](https://date-fns.org/docs/) for parsing/manipulating the dates. If you want
* more information on the date tokens to use, refer to
* [their format documentation](https://date-fns.org/docs/format).
*
* - The date helper format takes the following arguments:
*
* ```typescript
* (
* date: string,
* format: string,
* offsetAmount?: number,
* offsetType?: "years" | "months" | "weeks" | "days" | "hours" | "minutes" | "seconds"
* )
* ```
*
* - **The now helper** (for current time) takes the same arguments, minus the first one (`date`) as it is implicitly
* the current date.
*
* @see {@link DefaultHelpers}
* @see {@link CaseHelpers}
* @see {@link ScaffoldConfig}
*
* @category Helpers
*/
export type DateHelpers = "date" | "now"
/**
* The names of all the available helper functions in templates.
* Simple-Scaffold provides some built-in text transformation filters usable by Handlebars.js.
*
* For example, you may use `{{ snakeCase name }}` inside a template file or filename, and it will
* replace `My Name` with `my_name` when producing the final value.
*
* @see {@link CaseHelpers}
* @see {@link DateHelpers}
* @see {@link ScaffoldConfig}
*
* @category Helpers
*/
export type DefaultHelpers = CaseHelpers | DateHelpers
/**
* Helper function, see https://handlebarsjs.com/guide/#custom-helpers
*
* @category Helpers
*/
export type Helper = HelperDelegate
/**
* The amount of information to log when generating scaffold.
* When not `none`, the selected level will be the lowest level included.
*
* For example, level `info` will include `info`, `warning` and `error`, but not `debug`; and `warning` will only
* show `warning` and `error`, but not `info` or `debug`.
*
* @default `info`
*
* @category Logging (const)
*/
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.
*
* For example, level `info` will include `info`, `warning` and `error`, but not `debug`; and `warning` will only
* show `warning` and `error`, but not `info` or `debug`.
*
* @default `info`
*
* @category Logging (type)
*/
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]
/**
* A function that takes path information about file, and returns a value of type `T`
*
* @template T The return type for the function
* @param {string} fullPath The full path of the current file
* @param {string} basedir The directory containing the current file
* @param {string} basename The name of the file
*
* @returns {T} A return value
*
* @category Config
*/
export type FileResponseHandler<T> = (fullPath: string, basedir: string, basename: string) => T
/**
* Represents a response for file path information.
* Can either be:
*
* 1. `T` - static value
* 2. A function with the following signature which returns `T`:
* ```typescript
* (fullPath: string, basedir: string, basename: string) => T
* ```
*
* @see {@link FileResponseHandler}
*
* @category Config
* */
export type FileResponse<T> = T | FileResponseHandler<T>
/**
* The Scaffold config for CLI
* Contains less and more specific options than {@link ScaffoldConfig}
*/
export type 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
/** 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 */
createSubFolder?: boolean
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
/**
* 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
/** The git repository to use to fetch the config file */
git?: string
/** Display version */
version: boolean
overwrite?: FileResponse<boolean>
quiet?: boolean
verbose?: LogLevel
dryRun?: boolean
}
export interface ScaffoldCmdConfig {
name: string
templates: string[]
output: string
createSubFolder: boolean
data?: Record<string, string>
overwrite: boolean
quiet: boolean
dryRun: boolean
}
/**
* A mapping of scaffold template keys to their configurations.
*
* Each configuration is a {@link ScaffoldConfig} object.
*
* The key is the name of the template, and the value is the configuration for that template.
*
* When no template key is provided to the scaffold command, the "default" template is used.
*
* @see {@link ScaffoldConfig}
*
* @category Config
*/
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
*
* @category Config
*/
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"> & { tmpPath: string }
/** @internal */
export type MinimalConfig = Pick<ScaffoldCmdConfig, "name" | "key">

View File

@@ -1,17 +1,111 @@
import { Resolver } from "./types"
import path from "path"
import { F_OK } from "constants"
import { FileResponse, FileResponseFn, LogLevel, ScaffoldConfig } 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
export function handleErr(err: NodeJS.ErrnoException | null): void {
const helpers = {
camelCase,
snakeCase,
startCase,
kebabCase,
hyphenCase: kebabCase,
pascalCase,
}
for (const helperName in helpers) {
Handlebars.registerHelper(helperName, helpers[helperName as keyof typeof helpers])
}
export function handleErr(err: NodeJS.ErrnoException | null) {
if (err) throw err
}
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(options: ScaffoldConfig, level: LogLevel, ...obj: any[]) {
if (options.quiet || (options.verbose ?? LogLevel.Info) > level) {
return
}
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]]
console["log"](...obj.map((i) => (typeof i === "object" ? chalkFn(JSON.stringify(i, undefined, 1)) : chalkFn(i))))
// console["log"](...obj)
}
export function wrapNoopResolver<T, R = T>(value: Resolver<T, R>): Resolver<T, R> {
if (typeof value === "function") {
return value
export async function createDirIfNotExists(dir: string, options: ScaffoldConfig): Promise<void> {
const parentDir = path.dirname(dir)
if (!(await pathExists(parentDir))) {
await createDirIfNotExists(parentDir, options)
}
return (_) => value
if (!(await pathExists(dir))) {
try {
await mkdir(dir)
return
} catch (e: any) {
if (e.code !== "EEXIST") {
throw e
}
return
}
}
}
export function getOptionValueForFile<T>(
filePath: string,
data: Record<string, string>,
fn: FileResponse<T>,
defaultValue?: T
): T {
if (typeof fn !== "function") {
return defaultValue ?? (fn as T)
}
return (fn as FileResponseFn<T>)(
filePath,
path.dirname(handlebarsParse(filePath, data)),
path.basename(handlebarsParse(filePath, data))
)
}
export function handlebarsParse(templateBuffer: Buffer | string, data: Record<string, string>) {
const parser = Handlebars.compile(templateBuffer.toString(), { noEscape: true })
const outputContents = parser(data)
return outputContents
}
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) {
return template.replace(/\*/g, "").replace(/\/\//g, "/")
}

View File

@@ -1,169 +0,0 @@
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,
version: 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,
},
`/tmp/scaffold-config-${Date.now()}`,
),
).toEqual(blankCliConf)
})
describe("appendData", () => {
test("appends", async () => {
const result = await parseConfigFile(
{
...blankCliConf,
appendData: { key: "value" },
},
`/tmp/scaffold-config-${Date.now()}`,
)
expect(result?.data?.key).toEqual("value")
})
test("overwrites existing value", async () => {
const result = await parseConfigFile(
{
...blankCliConf,
data: { num: "123" },
appendData: { num: "1234" },
},
`/tmp/scaffold-config-${Date.now()}`,
)
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,
tmpPath: `/tmp/scaffold-config-${Date.now()}`,
})
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)
})
})
}
})
})

View File

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

@@ -2,11 +2,6 @@ import mockFs from "mock-fs"
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/parser"
import { join } from "path"
import * as dateFns from "date-fns"
import crypto from "crypto"
const fileStructNormal = {
input: {
@@ -14,13 +9,6 @@ const fileStructNormal = {
},
output: {},
}
const fileStructWithBinary = {
input: {
"{{name}}.txt": "Hello, my app is {{name}}",
"{{name}}.bin": crypto.randomBytes(10000),
},
output: {},
}
const fileStructWithData = {
input: {
@@ -31,60 +19,21 @@ const fileStructWithData = {
const fileStructNested = {
input: {
"{{name}}-1.txt": "This should be in root",
"{{pascalCase name}}": {
"{{name}}-1.text": "This should be in root",
"{{Name}}": {
"{{name}}-2.txt": "Hello, my value is {{value}}",
moreNesting: {
"{{name}}-3.txt": "Hi! My value is actually NOT {{value}}!",
},
},
},
output: {},
}
const fileStructSubdirTransformer = {
input: {
"{{name}}.txt": "Hello, my app is {{name}}",
},
output: {},
}
const defaultHelperNames = Object.keys(defaultHelpers)
const fileStructHelpers = {
input: {
defaults: defaultHelperNames.reduce<Record<string, string>>(
(all, cur) => ({ ...all, [cur + ".txt"]: `{{ ${cur} name }}` }),
{},
),
custom: {
"add1.txt": "{{ add1 name }}",
},
},
output: {},
}
const fileStructDates = {
input: {
"now.txt": "Today is {{ now 'mmm' }}, time is {{ now 'HH:mm' }}",
"offset.txt": "Yesterday was {{ now 'mmm' -1 'days' }}, time is {{ now 'HH:mm' -1 'days' }}",
"custom.txt": "Custom date is {{ date customDate 'mmm' }}, time is {{ date customDate 'HH:mm' }}",
},
output: {},
}
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()
})
}
@@ -99,10 +48,11 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
logLevel: "none",
quiet: true,
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
const data = readFileSync(process.cwd() + "/output/app_name.txt")
expect(data.toString()).toBe("Hello, my app is app_name")
})
test("should create with config", async () => {
@@ -110,32 +60,14 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
subdir: true,
logLevel: "none",
createSubFolder: true,
quiet: true,
})
const data = readFileSync(join(process.cwd(), "output", "app_name", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
const data = readFileSync(process.cwd() + "/output/app_name/app_name.txt")
expect(data.toString()).toBe("Hello, my app is app_name")
})
}),
)
describe(
"binary files",
withMock(fileStructWithBinary, () => {
test("should copy as-is", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
logLevel: "none",
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
const dataBin = readFileSync(join(process.cwd(), "output", "app_name.bin"))
expect(dataBin).toEqual(fileStructWithBinary.input["{{name}}.bin"])
})
}),
})
)
describe(
@@ -147,7 +79,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "1" },
logLevel: "none",
quiet: true,
})
await Scaffold({
@@ -155,11 +87,11 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "2" },
logLevel: "none",
quiet: true,
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my value is 1")
const data = readFileSync(process.cwd() + "/output/app_name.txt")
expect(data.toString()).toBe("Hello, my value is 1")
})
test("should overwrite with config", async () => {
@@ -168,7 +100,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "1" },
logLevel: "none",
quiet: true,
})
await Scaffold({
@@ -177,25 +109,21 @@ describe("Scaffold", () => {
templates: ["input"],
data: { value: "2" },
overwrite: true,
logLevel: "none",
quiet: true,
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my value is 2")
const data = readFileSync(process.cwd() + "/output/app_name.txt")
expect(data.toString()).toBe("Hello, my value is 2")
})
}),
})
)
describe(
"errors",
withMock(fileStructNormal, () => {
let consoleMock1: jest.SpyInstance
let consoleMock: jest.SpyInstance
beforeAll(() => {
consoleMock1 = jest.spyOn(console, "error").mockImplementation(() => void 0)
})
afterAll(() => {
consoleMock1.mockRestore()
consoleMock = jest.spyOn(console, "error").mockImplementation(() => void 0)
})
test("should throw for bad input", async () => {
@@ -205,50 +133,17 @@ describe("Scaffold", () => {
output: "output",
templates: ["non-existing-input"],
data: { value: "1" },
logLevel: "none",
}),
quiet: true,
})
).rejects.toThrow()
await expect(
Scaffold({
name: "app_name",
output: "output",
templates: ["non-existing-input/non-existing-file.txt"],
data: { value: "1" },
logLevel: "none",
}),
).rejects.toThrow()
expect(() => readFileSync(join(process.cwd(), "output", "app_name.txt"))).toThrow()
})
}),
)
describe(
"dry run",
withMock(fileStructNormal, () => {
let consoleMock1: jest.SpyInstance
beforeAll(() => {
consoleMock1 = jest.spyOn(console, "error").mockImplementation(() => void 0)
expect(() => readFileSync(process.cwd() + "/output/app_name.txt")).toThrow()
})
afterAll(() => {
consoleMock1.mockRestore()
consoleMock.mockRestore()
})
test("should not write to disk", async () => {
Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
data: { value: "1" },
logLevel: "none",
dryRun: true,
})
expect(() => readFileSync(join(process.cwd(), "output", "app_name.txt"))).toThrow()
})
}),
})
)
describe(
@@ -257,15 +152,15 @@ describe("Scaffold", () => {
test("should allow override function", async () => {
await Scaffold({
name: "app_name",
output: (_, __, basename) => join("custom-output", `${basename.split(".")[0]}`),
output: (fullPath, basedir, basename) => `custom-output/${basename.split(".")[0]}`,
templates: ["input"],
data: { value: "1" },
logLevel: "none",
quiet: true,
})
const data = readFileSync(join(process.cwd(), "/custom-output/app_name/app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
const data = readFileSync(process.cwd() + "/custom-output/app_name/app_name.txt")
expect(data.toString()).toBe("Hello, my app is app_name")
})
}),
})
)
describe(
@@ -274,229 +169,15 @@ describe("Scaffold", () => {
test("should maintain input structure on output", async () => {
await Scaffold({
name: "app_name",
output: "output",
output: "./",
templates: ["input"],
data: { value: "1" },
logLevel: "none",
quiet: true,
})
const rootDir = readdirSync(join(process.cwd(), "output"))
const dir = readdirSync(join(process.cwd(), "output", "AppName"))
const nestedDir = readdirSync(join(process.cwd(), "output", "AppName", "moreNesting"))
expect(rootDir).toHaveProperty("length")
const dir = readdirSync(process.cwd())
expect(dir).toHaveProperty("length")
expect(nestedDir).toHaveProperty("length")
const rootFile = readFileSync(join(process.cwd(), "output", "app_name-1.txt"))
const oneDeepFile = readFileSync(join(process.cwd(), "output", "AppName/app_name-2.txt"))
const twoDeepFile = readFileSync(join(process.cwd(), "output", "AppName/moreNesting/app_name-3.txt"))
expect(rootFile.toString()).toEqual("This should be in root")
expect(oneDeepFile.toString()).toEqual("Hello, my value is 1")
expect(twoDeepFile.toString()).toEqual("Hi! My value is actually NOT 1!")
})
}),
)
describe(
"capitalization helpers",
withMock(fileStructHelpers, () => {
const _helpers: Record<string, (text: string) => string> = {
add1: (text) => text + " 1",
}
test("should work", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
logLevel: "none",
helpers: _helpers,
})
const results = {
camelCase: "appName",
snakeCase: "app_name",
startCase: "App Name",
kebabCase: "app-name",
hyphenCase: "app-name",
pascalCase: "AppName",
lowerCase: "app_name",
upperCase: "APP_NAME",
}
for (const key in results) {
const file = readFileSync(join(process.cwd(), "output", "defaults", `${key}.txt`))
expect(file.toString()).toEqual(results[key as keyof typeof results])
}
})
}),
)
describe(
"date helpers",
withMock(fileStructDates, () => {
test("should work", async () => {
const now = new Date()
const yesterday = dateFns.add(new Date(), { days: -1 })
const customDate = dateFns.formatISO(dateFns.add(new Date(), { days: -1 }))
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
logLevel: "none",
data: { customDate },
})
const nowFile = readFileSync(join(process.cwd(), "output", "now.txt"))
const offsetFile = readFileSync(join(process.cwd(), "output", "offset.txt"))
const customFile = readFileSync(join(process.cwd(), "output", "custom.txt"))
// "now.txt": "Today is {{ now 'mmm' }}, time is {{ now 'HH:mm' }}",
// "offset.txt": "Yesterday was {{ now 'mmm' -1 'days' }}, time is {{ now 'HH:mm' -1 'days' }}",
// "custom.txt": "Custom date is {{ date customDate 'mmm' }}, time is {{ date customDate 'HH:mm' }}",
expect(nowFile.toString()).toEqual(
`Today is ${dateFns.format(now, "mmm")}, time is ${dateFns.format(now, "HH:mm")}`,
)
expect(offsetFile.toString()).toEqual(
`Yesterday was ${dateFns.format(yesterday, "mmm")}, time is ${dateFns.format(yesterday, "HH:mm")}`,
)
expect(customFile.toString()).toEqual(
`Custom date is ${dateFns.format(dateFns.parseISO(customDate), "mmm")}, time is ${dateFns.format(
dateFns.parseISO(customDate),
"HH:mm",
)}`,
)
})
}),
)
describe(
"custom helpers",
withMock(fileStructHelpers, () => {
const _helpers: Record<string, (text: string) => string> = {
add1: (text) => text + " 1",
}
test("should work", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
logLevel: "none",
helpers: _helpers,
})
const results = {
add1: "app_name 1",
}
for (const key in results) {
const file = readFileSync(join(process.cwd(), "output", "custom", `${key}.txt`))
expect(file.toString()).toEqual(results[key as keyof typeof results])
}
})
}),
)
describe(
"transform subfolder",
withMock(fileStructSubdirTransformer, () => {
test("should work with no helper", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
subdir: true,
logLevel: "none",
})
const data = readFileSync(join(process.cwd(), "output", "app_name", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
})
test("should work with default helper", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
subdir: true,
logLevel: "none",
subdirHelper: "upperCase",
})
const data = readFileSync(join(process.cwd(), "output", "APP_NAME", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
})
test("should work with custom helper", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
subdir: true,
logLevel: "none",
subdirHelper: "test",
helpers: {
test: () => "REPLACED",
},
})
const data = readFileSync(join(process.cwd(), "output", "REPLACED", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
})
}),
)
describe(
"before write",
withMock(fileStructNormal, () => {
test("should work with no callback", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
logLevel: "none",
data: {
value: "value",
},
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
})
test("should work with custom callback", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
logLevel: "none",
data: {
value: "value",
},
beforeWrite: (content, beforeContent, outputPath) =>
[content.toString().toUpperCase(), beforeContent, outputPath].join(", "),
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(data.toString()).toEqual(
[
"Hello, my app is app_name".toUpperCase(),
fileStructNormal.input["{{name}}.txt"],
join(process.cwd(), "output", "app_name.txt"),
].join(", "),
)
})
test("should work with undefined response custom callback", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
logLevel: "none",
data: {
value: "value",
},
beforeWrite: () => undefined,
})
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(data.toString()).toEqual("Hello, my app is app_name")
})
}),
})
)
})

View File

@@ -1,21 +0,0 @@
import { handleErr, resolve } from "../src/utils"
describe("utils", () => {
describe("resolve", () => {
test("should resolve function", () => {
expect(resolve(() => 1, null)).toBe(1)
expect(resolve((x) => x, 2)).toBe(2)
})
test("should resolve value", () => {
expect(resolve(1, null)).toBe(1)
expect(resolve(2, 1)).toBe(2)
})
})
describe("handleErr", () => {
test("should throw error", () => {
expect(() => handleErr({ name: "test", message: "test" })).toThrow()
expect(() => handleErr(null as never)).not.toThrow()
})
})
})

View File

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

View File

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

2952
yarn.lock Normal file

File diff suppressed because it is too large Load Diff