Compare commits

...

14 Commits

Author SHA1 Message Date
semantic-release-bot
0be5e63784 chore(release): 2.0.2-pre.1 [skip ci]
## [2.0.2-pre.1](https://github.com/chenasraf/simple-scaffold/compare/v2.0.1...v2.0.2-pre.1) (2024-02-03)

### Bug Fixes

* try to await scaffold before finally ([b0a6bf7](b0a6bf7021))
2024-02-03 22:15:03 +00:00
b0a6bf7021 fix: try to await scaffold before finally 2024-02-04 00:14:25 +02:00
493cacb824 docs: update readme 2024-02-04 00:14:25 +02:00
ae2a27f258 docs: update logo 2024-02-04 00:14:25 +02:00
b16ae211ac docs: update readme 2024-02-04 00:14:25 +02:00
435948a7c7 docs: add logo, update docs 2024-02-04 00:14:25 +02:00
51c59175ea docs: update readme 2024-02-04 00:14:25 +02:00
26db546ca4 docs: update readme 2024-02-04 00:14:25 +02:00
ee0857a896 chore: version output + docs update 2024-02-04 00:14:25 +02:00
1fbd0e5393 docs: fix github link 2024-02-04 00:14:25 +02:00
cd8b1432bc ci: clean up release config 2024-02-04 00:14:25 +02:00
85b03073ab docs: fix edit link 2024-02-04 00:14:25 +02:00
72fdb655e1 ci: fix pack step order 2024-02-04 00:14:25 +02:00
bda19833db chore: update package.json spec 2024-02-04 00:14:25 +02:00
18 changed files with 250 additions and 136 deletions

182
README.md
View File

@@ -1,4 +1,6 @@
<h1 align="center">Simple Scaffold</h1>
<p align="center">
<img src="https://chenasraf.github.io//simple-scaffold/img/logo-lg.png" alt="Logo" />
</p>
<h2 align="center">
@@ -31,54 +33,76 @@ lifting for you and start building your projects faster and more efficiently tod
---
## Quick Start
## Documentation
### Local Templates
See full documentation [here](https://chenasraf.github.io/simple-scaffold).
The fastest way to get started is to use `npx` to immediately start a scaffold process.
- [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)
Prepare any templates you want to use - for example, in the directory `templates/component`; and use
that in the CLI args. Here is a simple example file:
## Getting Started
Simple Scaffold will maintain any file and directory structure you try to generate.
### Cheat Sheet
`templates/component/{{ pascalName name }}.tsx`
A quick rundown of common usage scenarios:
```tsx
// Created: {{ now 'yyyy-MM-dd' }}
import React from 'react'
- Remote template config file on GitHub:
export default {{pascalCase name}}: React.FC = (props) => {
return (
<div className="{{camelCase name}}">{{pascalCase name}} Component</div>
)
}
```
```sh
npx simple-scaffold -g username/repository -c scaffold.js -k component NewComponentName
```
To generate the template output, run:
- 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
# generate single component
$ npx simple-scaffold@latest \
-t templates/component -o src/components PageWrapper
$ 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
```
This will immediately create the following file: `src/components/PageWrapper.tsx`
By default, the template name is set to `default` when the `--key` option is not provided.
```tsx
// Created: 2077-01-01
import React from 'react'
export default PageWrapper: React.FC = (props) => {
return (
<div className="pageWrapper">PageWrapper Component</div>
)
}
```
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).
### Configuration Files
You can also use a config file to more easily maintain all your scaffold definitions.
You can use a config file to more easily maintain all your scaffold definitions.
`scaffold.config.js`
@@ -99,66 +123,61 @@ module.exports = {
Then call your scaffold like this:
```sh
$ npx simple-scaffold@latest -c scaffold.config.js PageWrapper
$ npx simple-scaffold -c scaffold.config.js PageWrapper
```
This will allow you to avoid needing to remember which configs are needed or to store them in a
1-liner in `packqge.json` which can get pretty long and messy, which is harder to maintain.
one-liner in `package.json` which can get pretty long and messy, and harder to maintain.
Also, this allows you to define more complex scaffolds with logic without having to use the Node.js
API directly. (Of course you always have the option to still do so if you wish)
See more at the [CLI documentation](https://chenasraf.github.io/simple-scaffold/docs/usage/cli) and
[Configuration Files](https://chenasraf.github.io/simple-scaffold/docs/usage/configuration_files).
More information can be found at the
[Configuration Files documentation](https://chenasraf.github.io/simple-scaffold/docs/usage/configuration_files).
### Remote Configurations
### Templates Structure
Another quick way to start is to re-use someone else's (or your own) work using a template
repository.
Templates are **any file** in the a directory given to `--templates`.
A remote config can be loaded in one of these ways:
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/).
- If it's on GitHub, you can use `-g user/repository_name`
- If it's on another git server (such as GitLab), you can use
`-g https://example.com/user/repository_name.git`
`templates/component/{{ pascalName name }}.tsx`
Configurations can hold multiple scaffold groups. Each group can be accessed using its key by
supplying the `--key` or `-k` argument, like so:
```tsx
// Created: {{ now 'yyyy-MM-dd' }}
import React from 'react'
```sh
-g user/repository_name -c scaffold.js -k key_name`.
export default {{pascalCase name}}: React.FC = (props) => {
return (
<div className="{{camelCase name}}">{{pascalCase name}} Component</div>
)
}
```
Here is an example for loading the example component templates in this very repository:
To generate the template output once without saving a configuration file, run:
```sh
$ npx simple-scaffold@latest \
-g chenasraf/simple-scaffold \
-k component \
PageWrapper
# equivalent to:
$ npx simple-scaffold@latest \
-g https://github.com/chenasraf/simple-scaffold.git \
-c scaffold.config.js \
-k component \
# generate single component
$ npx simple-scaffold \
-t templates/component \
-o src/components \
PageWrapper
```
When template name (`-k component`) is omitted, `default` is used.
This will immediately create the following file: `src/components/PageWrapper.tsx`
See more at the [CLI documentation](https://chenasraf.github.io/simple-scaffold/docs/usage/cli) and
[Configuration Files](https://chenasraf.github.io/simple-scaffold/docs/usage/configuration_files).
```tsx
// Created: 2077-01-01
import React from 'react'
## 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)
export default PageWrapper: React.FC = (props) => {
return (
<div className="pageWrapper">PageWrapper Component</div>
)
}
```
## Contributing
@@ -189,22 +208,9 @@ If you are a developer and want to contribute code, here are some starting tips:
Some tips on getting around the code:
- Use `pnpm dev` for development - it runs TypeScript compile in watch mode, allowing you to make
changes and immediately be able to try them using `pnpm cmd`.
- Use `pnpm build` to build the output once
- Use `pnpm test` to run tests
- Use `pnpm cmd` to use the CLI feature of Simple Scaffold from within the root directory, enabling
you to test different behaviors. See `pnpm cmd -h` for more information.
> This requires an updated build, and does not trigger one itself. From here you have several
> options:
>
> - Run `pnpm dev` to watch for file changes and build automatically
> - Run `pnpm build` before running this to trigger a one-time build
> - Run `pnpm build-cmd` which triggers a build right before running `pnpm cmd` automatically with
> the rest of the given arguments.
- Use `pnpm build-docs` to build the documentation once
- Use `pnpm watch-docs` to start docs in watch mode
- To see the documentation, currently you have to serve the directory yourself with a static web
server (like node's built in serve, VS code's "Go Live" mode, etc)
- 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

View File

@@ -28,6 +28,7 @@ To see this and more information anytime, add the `-h` or `--help` flag to your
| `--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:

View File

@@ -5,7 +5,7 @@ import type * as Preset from "@docusaurus/preset-classic"
const config: Config = {
title: "Simple Scaffold",
tagline: "Generate any file structure - from single components to entire app boilerplates, with a single command.",
favicon: "img/favicon.ico",
favicon: "img/favicon.svg",
// Set the production url of your site here
url: "https://chenasraf.github.io",
@@ -64,13 +64,7 @@ const config: Config = {
sidebarPath: "./sidebars.ts",
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl: "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/",
},
blog: {
showReadingTime: true,
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl: "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/",
editUrl: "https://github.com/chenasraf/simple-scaffold/blob/master/docs",
},
theme: {
customCss: "./src/css/custom.css",
@@ -127,7 +121,7 @@ const config: Config = {
position: "right",
},
{
href: "https://github.com/facebook/docusaurus",
href: "https://github.com/chenasraf/simple-scaffold",
label: "GitHub",
position: "right",
},

View File

@@ -22,3 +22,13 @@
justify-content: center;
gap: 2rem;
}
.heroImage {
margin-bottom: 1.5rem;
}
.logo {
width: 100%;
max-width: 300px;
margin: 0 auto;
}

View File

@@ -12,10 +12,12 @@ function HomepageHeader() {
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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

20
docs/static/img/favicon.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 265 KiB

BIN
docs/static/img/logo-lg.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

20
docs/static/img/logo-lg.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 281 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 432 KiB

View File

@@ -1,14 +1,19 @@
{
"name": "simple-scaffold",
"version": "2.0.1",
"version": "2.0.2-pre.1",
"description": "Generate any file structure - from single components to entire app boilerplates, with a single command.",
"homepage": "https: //chenasraf.github.io/simple-scaffold",
"repository": "https://github.com/chenasraf/simple-scaffold.git",
"author": "Chen Asraf <contact@casraf.dev>",
"repository": {
"type": "git",
"url": "https://github.com/chenasraf/simple-scaffold.git"
},
"author": "Chen Asraf <contact@casraf.dev> (https://casraf.dev)",
"license": "MIT",
"main": "index.js",
"bin": "cmd.js",
"packageManager": "pnpm@8.6.2",
"bin": {
"simple-scaffold": "cmd.js"
},
"packageManager": "pnpm@8.15.1",
"keywords": [
"javascript",
"cli",

View File

@@ -24,6 +24,12 @@ module.exports = {
// pkgRoot: 'doc',
// },
// ]
[
"@semantic-release/exec",
{
publish: "cd ./dist && pnpm pack --pack-destination=../",
},
],
[
"@semantic-release/npm",
{
@@ -31,18 +37,21 @@ module.exports = {
pkgRoot: "dist",
},
],
[
"@semantic-release/exec",
{
publish: "cd ./dist && pnpm pack --pack-destination=../",
},
],
[
"@semantic-release/github",
{
assets: ["*.tgz"],
},
],
branch === "master"
? [
"@semantic-release/changelog",
{
changelogFile: "CHANGELOG.md",
changelogTitle: "# Change Log",
},
]
: undefined,
[
"@semantic-release/git",
{
@@ -56,16 +65,5 @@ module.exports = {
// verifyReleaseCmd: 'echo ${nextRelease.version} > .VERSION',
// },
// ],
],
}
if (branch === "master") {
const gitIdx = module.exports.plugins.findIndex((plugin) => plugin[0] === "@semantic-release/git")
module.exports.plugins.splice(gitIdx, 0, [
"@semantic-release/changelog",
{
changelogFile: "CHANGELOG.md",
changelogTitle: "# Change Log",
},
])
].filter(Boolean),
}

View File

@@ -8,24 +8,33 @@ 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")
args.includes("--config") || args.includes("-c") || args.includes("--git") || args.includes("-g") || isVersionFlag
return massarg<ScaffoldCmdConfig>({
name: pkg.name,
description: pkg.description,
})
.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()}`)
const parsed = await parseConfigFile(config, tmpPath)
try {
return Scaffold(parsed)
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 })
}
})
@@ -36,7 +45,7 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
"Name to be passed to the generated files. `{{name}}` and other data parameters inside " +
"contents and file names will be replaced accordingly. You may omit the `--name` or `-n` for this specific option.",
isDefault: true,
required: true,
required: !isVersionFlag,
})
.option({
name: "config",
@@ -126,7 +135,13 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
"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) => v.toLowerCase(),
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",
@@ -136,6 +151,11 @@ export async function parseCliArgs(args = process.argv.slice(2)) {
"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",

View File

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

View File

@@ -128,6 +128,7 @@ Scaffold.fromConfig = async function (
subdir: false,
quiet: false,
config: pathOrUrl,
version: false,
...config,
}
const tmpPath = path.resolve(os.tmpdir(), `scaffold-config-${Date.now()}`)

View File

@@ -257,6 +257,18 @@ export type DefaultHelpers = CaseHelpers | DateHelpers
*/
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",
@@ -276,17 +288,14 @@ export const LogLevel = {
/**
* The amount of information to log when generating scaffold.
* When not `None`, the selected level will be the lowest level included.
* When not `none`, the selected level will be the lowest level included.
*
* For example, level `Info` (2) will include `Info`, `Warning` and `Error`, but not `Debug`; and `Warning` will only
* show `Warning` and `Error`.
* 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`.
*
* You may use either the number or the name of the level.
* For example, `2` or `info` are both valid.
* @default `info`
*
* @default `2 (info)`
*
* @category Logging
* @category Logging (type)
*/
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel]
@@ -324,7 +333,7 @@ export type FileResponse<T> = T | FileResponseHandler<T>
* The Scaffold config for CLI
* Contains less and more specific options than {@link ScaffoldConfig}
*/
export interface ScaffoldCmdConfig {
export type ScaffoldCmdConfig = {
/** The name of the scaffold template to use. */
name: string
/** The templates to use for generation */
@@ -357,6 +366,8 @@ export interface ScaffoldCmdConfig {
key?: string
/** The git repository to use to fetch the config file */
git?: string
/** Display version */
version: boolean
}
/**
@@ -369,14 +380,19 @@ export interface ScaffoldCmdConfig {
* 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:
/**
* 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>

View File

@@ -30,6 +30,7 @@ const blankCliConf: ScaffoldCmdConfig = {
subdir: false,
dryRun: false,
quiet: false,
version: false,
}
const blankConfig: ScaffoldCmdConfig = {