mirror of
https://github.com/chenasraf/simple-scaffold.git
synced 2026-05-18 01:29:09 +00:00
Compare commits
13 Commits
v1.1.0
...
v1.1.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce9fededf0 | ||
|
|
4653428bda | ||
|
|
19b2203f30 | ||
|
|
3f103cd4c2 | ||
|
|
ec5503bc3e | ||
|
|
19ad60c3cb | ||
|
|
f04dd7e487 | ||
|
|
4c9a838ebe | ||
|
|
6700708e1d | ||
|
|
2727bd526c | ||
|
|
834a805794 | ||
|
|
ba9434f504 | ||
|
|
59353f4121 |
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -3,7 +3,6 @@
|
||||
"npm.packageManager": "yarn",
|
||||
"cSpell.words": [
|
||||
"massarg",
|
||||
"myname",
|
||||
"nobrace",
|
||||
"nocomment",
|
||||
"nodir",
|
||||
|
||||
113
README.md
113
README.md
@@ -1,4 +1,4 @@
|
||||
# Simple Scaffold
|
||||
# simple-scaffold
|
||||
|
||||
Simple Scaffold allows you to generate any set of files in the easiest way possible with simple commands.
|
||||
|
||||
@@ -14,7 +14,7 @@ other custom data) inside the paths or contents of the files using Handlebars.js
|
||||
<details>
|
||||
<summary>Table of contents</summary>
|
||||
|
||||
- [Simple Scaffold](#simple-scaffold)
|
||||
- [simple-scaffold](#simple-scaffold)
|
||||
- [Install](#install)
|
||||
- [Use as a command line tool](#use-as-a-command-line-tool)
|
||||
- [Command Line Options](#command-line-options)
|
||||
@@ -23,10 +23,7 @@ other custom data) inside the paths or contents of the files using Handlebars.js
|
||||
- [Preparing files](#preparing-files)
|
||||
- [Template files](#template-files)
|
||||
- [Variable/token replacement](#variabletoken-replacement)
|
||||
- [Built-in Helpers](#built-in-helpers)
|
||||
- [Capitalization Helpers](#capitalization-helpers)
|
||||
- [Date helpers](#date-helpers)
|
||||
- [Custom Helpers](#custom-helpers)
|
||||
- [Helpers](#helpers)
|
||||
- [Examples](#examples)
|
||||
- [Command Example](#command-example)
|
||||
- [Example Scaffold Input](#example-scaffold-input)
|
||||
@@ -118,9 +115,8 @@ You can also add this as a script in your `package.json`:
|
||||
|
||||
## 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 Scaffold function when you are ready to start.
|
||||
|
||||
You can also build the scaffold yourself, if you want to create more complex arguments or scaffold groups.
|
||||
Simply pass a config object to the Scaffold function when you are ready to start.
|
||||
The config takes similar arguments to the command line:
|
||||
|
||||
```typescript
|
||||
@@ -149,11 +145,11 @@ const scaffold = Scaffold(config)
|
||||
In addition to all the options available in the command line, there are some Node/JS-specific
|
||||
options available:
|
||||
|
||||
| Option | Type | Description |
|
||||
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `output` | | In addition to being passed the same as CLI, it may also be passed a function for each input file to output into a dynamic path: `{ output: (fullPath, baseDir, baseName) => path.resolve(baseDir, baseName) }` |
|
||||
| `helpers` | `Record<string, (string) => string>` | Helpers are simple functions that transform your `data` variables into other values. See [Helpers](#helpers) for the list of default helpers, or add your own to be loaded into the template parser. |
|
||||
| `beforeWrite` | `(content: Buffer, rawContent: Buffer, outputPath: string) => Promise<String \| Buffer \| undefined> \| String \| Buffer \| undefined` | Supply this function to override the final output contents of each of your files, allowing you to add more pre-processing to your generator pipeline. The return value of this function will replace the output content of the respective file, which you may discriminate (if needed) using the `outputPath` argument. |
|
||||
| Option | Type | Description |
|
||||
| ------------- | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `output` | | In addition to being passed the same as CLI, it may also be passed a function for each input file to output into a dynamic path: `{ output: (fullPath, baseDir, baseName) => path.resolve(baseDir, baseName) }` |
|
||||
| `helpers` | `Record<string, (string) => string>` | Helpers are simple functions that transform your `data` variables into other values. See [Helpers](#helpers) for the list of default helpers, or add your own to be loaded into the template parser. |
|
||||
| `beforeWrite` | `(content: Buffer, rawContent: Buffer, outputPath: string) => String \| Buffer \| undefined` | Supply this function to override the final output contents of each of your files. The return value of this function will replace the output content of the respective file, which you may discriminate (if needed) using the `outputPath` argument. |
|
||||
|
||||
## Preparing files
|
||||
|
||||
@@ -171,11 +167,11 @@ 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` |
|
||||
| Input template | Output path(s) |
|
||||
| ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| `./templates/{{ name }}.txt` | `src/AppName.txt` |
|
||||
| `./templates/directory` <br /><br /> Directory contents:<br /> <ol><li>`outer/{{name}}.txt`</li></li><li>`outer2/inner/{{name.txt}}`</li></ol> | `src/outer/AppName.txt`,<br />`src/outer2/inner/AppName.txt` |
|
||||
| `./templates/others/**/*.txt` <br /><br /> Directory contents:<br /> <ol><li>`outer/{{name}}.jpg`</li></li><li>`outer2/inner/{{name.txt}}`</li></ol> | `src/outer2/inner/AppName.txt` |
|
||||
|
||||
### Variable/token replacement
|
||||
|
||||
@@ -191,7 +187,7 @@ For example, using the following command:
|
||||
npx simple-scaffold@latest \
|
||||
--templates templates/components/{{name}}.jsx \
|
||||
--output src/components \
|
||||
--create-sub-folder true \
|
||||
-create-sub-folder true \
|
||||
MyComponent
|
||||
```
|
||||
|
||||
@@ -214,58 +210,28 @@ Your `data` will be pre-populated with the following:
|
||||
> [Handlebars.js Language Features](https://handlebarsjs.com/guide/#language-features) for more
|
||||
> information.
|
||||
|
||||
### Built-in Helpers
|
||||
### 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
|
||||
Here are the built-in helpers available for use:
|
||||
|
||||
| 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 |
|
||||
| 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
|
||||
> These helpers are available for any data property, not exclusive to `name`.
|
||||
|
||||
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
|
||||
@@ -277,11 +243,8 @@ config.helpers = {
|
||||
}
|
||||
```
|
||||
|
||||
All of the above helpers (built in and custom) will also be available to you when using
|
||||
`subFolderNameHelper` (`--sub-folder-name-helper`/`-sh`) as a possible value.
|
||||
|
||||
> To see more information on how helpers work and more features, see
|
||||
> [Handlebars.js docs](https://handlebarsjs.com/guide/#custom-helpers).
|
||||
These helpers will also be available to you when using `subFolderNameHelper` or
|
||||
`--sub-folder-name-helper` as a possible value.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -313,7 +276,7 @@ simple-scaffold MyComponent \
|
||||
```typescriptreact
|
||||
import React from 'react'
|
||||
|
||||
export default {{camelCase name}}: React.FC = (props) => {
|
||||
export default {{camelCase ame}}: React.FC = (props) => {
|
||||
return (
|
||||
<div className="{{className}}">{{camelCase name}} Component</div>
|
||||
)
|
||||
@@ -357,16 +320,6 @@ export default MyComponent: React.FC = (props) => {
|
||||
|
||||
## 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 would like to donate a bit to help keep the project
|
||||
alive, I would be very thankful!
|
||||
|
||||
<a href='https://ko-fi.com/casraf' target='_blank'>
|
||||
<img height='36' style='border:0px;height:36px;'
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "simple-scaffold",
|
||||
"version": "1.1.0",
|
||||
"description": "A simple command to generate any file structure, from single components to entire app boilerplates.",
|
||||
"version": "1.1.0-alpha.1",
|
||||
"description": "Create files based on templates",
|
||||
"repository": "https://github.com/chenasraf/simple-scaffold.git",
|
||||
"author": "Chen Asraf <inbox@casraf.com>",
|
||||
"license": "MIT",
|
||||
@@ -30,7 +30,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
"date-fns": "^2.28.0",
|
||||
"glob": "^7.1.3",
|
||||
"handlebars": "^4.7.7",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -50,4 +49,4 @@
|
||||
"ts-node": "^10.1.0",
|
||||
"typescript": "^4.3.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
makeRelativePath,
|
||||
registerHelpers,
|
||||
getTemplateGlobInfo,
|
||||
ensureFileExists,
|
||||
getFileList,
|
||||
getBasePath,
|
||||
copyFileTransformed,
|
||||
@@ -63,6 +64,7 @@ export async function Scaffold({ ...options }: ScaffoldConfig): Promise<void> {
|
||||
options,
|
||||
_template
|
||||
)
|
||||
await ensureFileExists(template, isDirOrGlob)
|
||||
const files = await getFileList(options, template)
|
||||
for (const inputFilePath of files) {
|
||||
if (await isDir(inputFilePath)) {
|
||||
|
||||
32
src/types.ts
32
src/types.ts
@@ -1,5 +1,3 @@
|
||||
import { HelperDelegate } from "handlebars/runtime"
|
||||
|
||||
export enum LogLevel {
|
||||
None = 0,
|
||||
Debug = 1,
|
||||
@@ -14,19 +12,17 @@ export type FileResponse<T> = T | FileResponseFn<T>
|
||||
|
||||
export type DefaultHelperKeys =
|
||||
| "camelCase"
|
||||
| "date"
|
||||
| "hyphenCase"
|
||||
| "kebabCase"
|
||||
| "lowerCase"
|
||||
| "now"
|
||||
| "pascalCase"
|
||||
| "snakeCase"
|
||||
| "startCase"
|
||||
| "kebabCase"
|
||||
| "hyphenCase"
|
||||
| "pascalCase"
|
||||
| "lowerCase"
|
||||
| "upperCase"
|
||||
|
||||
export type HelperKeys<T> = DefaultHelperKeys | T
|
||||
|
||||
export type Helper = HelperDelegate
|
||||
export type Helper = (text: string) => string
|
||||
|
||||
export interface ScaffoldConfig {
|
||||
/**
|
||||
@@ -98,7 +94,17 @@ export interface ScaffoldConfig {
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* See the list of all the built-in available helpers, or how to define your own in the [readme](https://github.com/chenasraf/simple-scaffold#built-in-helpers).
|
||||
* 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 |
|
||||
* | upperCase | `{{ upperCase name }}` | MYNAME |
|
||||
* | lowerCase | `{{ lowerCase name }}` | myname |
|
||||
*/
|
||||
helpers?: Record<string, Helper>
|
||||
|
||||
@@ -122,11 +128,7 @@ export interface ScaffoldConfig {
|
||||
* @returns `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>
|
||||
beforeWrite?(content: Buffer, rawContent: Buffer, outputPath: string): string | Buffer | undefined
|
||||
}
|
||||
export interface ScaffoldCmdConfig {
|
||||
name: string
|
||||
|
||||
65
src/utils.ts
65
src/utils.ts
@@ -9,15 +9,6 @@ import Handlebars from "handlebars"
|
||||
import { promises as fsPromises } from "fs"
|
||||
import chalk from "chalk"
|
||||
const { stat, access, mkdir } = fsPromises
|
||||
import dtAdd from "date-fns/add"
|
||||
import dtFormat from "date-fns/format"
|
||||
import dtParseISO from "date-fns/parseISO"
|
||||
|
||||
const dateFns = {
|
||||
add: dtAdd,
|
||||
format: dtFormat,
|
||||
parseISO: dtParseISO,
|
||||
}
|
||||
|
||||
import { glob } from "glob"
|
||||
import { promisify } from "util"
|
||||
@@ -32,50 +23,6 @@ export const defaultHelpers: Record<DefaultHelperKeys, Helper> = {
|
||||
pascalCase,
|
||||
lowerCase: (text) => text.toLowerCase(),
|
||||
upperCase: (text) => text.toUpperCase(),
|
||||
now: nowHelper,
|
||||
date: dateHelper,
|
||||
}
|
||||
|
||||
export function _dateHelper(date: Date, formatString: string): string
|
||||
export function _dateHelper(
|
||||
date: Date,
|
||||
formatString: string,
|
||||
durationDifference: number,
|
||||
durationType: keyof Duration
|
||||
): string
|
||||
export function _dateHelper(
|
||||
date: Date,
|
||||
formatString: string,
|
||||
durationDifference?: number,
|
||||
durationType?: keyof Duration
|
||||
): string {
|
||||
if (durationType && durationDifference !== undefined) {
|
||||
return dateFns.format(dateFns.add(date, { [durationType]: durationDifference }), formatString)
|
||||
}
|
||||
return dateFns.format(date, formatString)
|
||||
}
|
||||
|
||||
export function nowHelper(formatString: string): string
|
||||
export function nowHelper(formatString: string, durationDifference: number, durationType: keyof Duration): string
|
||||
export function nowHelper(formatString: string, durationDifference?: number, durationType?: keyof Duration): string {
|
||||
return _dateHelper(new Date(), formatString, durationDifference!, durationType!)
|
||||
}
|
||||
|
||||
export function dateHelper(date: string, formatString: string): string
|
||||
export function dateHelper(
|
||||
date: string,
|
||||
formatString: string,
|
||||
durationDifference: number,
|
||||
durationType: keyof Duration
|
||||
): string
|
||||
|
||||
export function dateHelper(
|
||||
date: string,
|
||||
formatString: string,
|
||||
durationDifference?: number,
|
||||
durationType?: keyof Duration
|
||||
): string {
|
||||
return _dateHelper(dateFns.parseISO(date), formatString, durationDifference!, durationType!)
|
||||
}
|
||||
|
||||
export function registerHelpers(options: ScaffoldConfig): void {
|
||||
@@ -245,6 +192,16 @@ export async function getTemplateGlobInfo(options: ScaffoldConfig, template: str
|
||||
return { nonGlobTemplate, origTemplate, isDirOrGlob, isGlob, template: _template }
|
||||
}
|
||||
|
||||
export async function ensureFileExists(template: string, isGlob: boolean): Promise<void> {
|
||||
if (!isGlob && !(await pathExists(template))) {
|
||||
const err: NodeJS.ErrnoException = new Error(`ENOENT, no such file or directory ${template}`)
|
||||
err.code = "ENOENT"
|
||||
err.path = template
|
||||
err.errno = -2
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export interface OutputFileInfo {
|
||||
inputPath: string
|
||||
outputPathOpt: string
|
||||
@@ -289,7 +246,7 @@ export async function copyFileTransformed(
|
||||
const templateBuffer = await readFile(inputPath)
|
||||
const unprocessedOutputContents = handlebarsParse(options, templateBuffer)
|
||||
const finalOutputContents = (
|
||||
(await options.beforeWrite?.(unprocessedOutputContents, templateBuffer, outputPath)) ?? unprocessedOutputContents
|
||||
options.beforeWrite?.(unprocessedOutputContents, templateBuffer, outputPath) ?? unprocessedOutputContents
|
||||
).toString()
|
||||
|
||||
if (!options.dryRun) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { readdirSync, readFileSync } from "fs"
|
||||
import { Console } from "console"
|
||||
import { defaultHelpers } from "../src/utils"
|
||||
import { join } from "path"
|
||||
import * as dateFns from "date-fns"
|
||||
|
||||
const fileStructNormal = {
|
||||
input: {
|
||||
@@ -53,16 +52,8 @@ const fileStructHelpers = {
|
||||
},
|
||||
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: {},
|
||||
}
|
||||
|
||||
// let logsTemp: any = []
|
||||
// let logMock: any
|
||||
function withMock(fileStruct: FileSystem.DirectoryItems, testFn: jest.EmptyFunction): jest.EmptyFunction {
|
||||
return () => {
|
||||
beforeEach(() => {
|
||||
@@ -94,7 +85,7 @@ describe("Scaffold", () => {
|
||||
verbose: 0,
|
||||
})
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
})
|
||||
|
||||
test("should create with config", async () => {
|
||||
@@ -107,7 +98,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name", "app_name.txt"))
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
})
|
||||
})
|
||||
)
|
||||
@@ -133,7 +124,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
|
||||
expect(data.toString()).toEqual("Hello, my value is 1")
|
||||
expect(data.toString()).toBe("Hello, my value is 1")
|
||||
})
|
||||
|
||||
test("should overwrite with config", async () => {
|
||||
@@ -155,7 +146,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
|
||||
expect(data.toString()).toEqual("Hello, my value is 2")
|
||||
expect(data.toString()).toBe("Hello, my value is 2")
|
||||
})
|
||||
})
|
||||
)
|
||||
@@ -183,43 +174,6 @@ describe("Scaffold", () => {
|
||||
})
|
||||
).rejects.toThrow()
|
||||
|
||||
await expect(
|
||||
Scaffold({
|
||||
name: "app_name",
|
||||
output: "output",
|
||||
templates: ["non-existing-input/non-existing-file.txt"],
|
||||
data: { value: "1" },
|
||||
verbose: 0,
|
||||
})
|
||||
).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)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
consoleMock1.mockRestore()
|
||||
})
|
||||
|
||||
test("should not write to disk", async () => {
|
||||
Scaffold({
|
||||
name: "app_name",
|
||||
output: "output",
|
||||
templates: ["input"],
|
||||
data: { value: "1" },
|
||||
verbose: 0,
|
||||
dryRun: true,
|
||||
})
|
||||
|
||||
expect(() => readFileSync(join(process.cwd(), "output", "app_name.txt"))).toThrow()
|
||||
})
|
||||
})
|
||||
@@ -237,7 +191,7 @@ describe("Scaffold", () => {
|
||||
verbose: 0,
|
||||
})
|
||||
const data = readFileSync(join(process.cwd(), "/custom-output/app_name/app_name.txt"))
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
})
|
||||
})
|
||||
)
|
||||
@@ -272,99 +226,56 @@ describe("Scaffold", () => {
|
||||
)
|
||||
|
||||
describe(
|
||||
"capitalization helpers",
|
||||
"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"],
|
||||
verbose: 0,
|
||||
helpers: _helpers,
|
||||
})
|
||||
describe("default helpers", () => {
|
||||
test("should work", async () => {
|
||||
await Scaffold({
|
||||
name: "app_name",
|
||||
output: "output",
|
||||
templates: ["input"],
|
||||
verbose: 0,
|
||||
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])
|
||||
}
|
||||
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 }))
|
||||
describe("custom helpers", () => {
|
||||
test("should work", async () => {
|
||||
await Scaffold({
|
||||
name: "app_name",
|
||||
output: "output",
|
||||
templates: ["input"],
|
||||
verbose: 0,
|
||||
helpers: _helpers,
|
||||
})
|
||||
|
||||
await Scaffold({
|
||||
name: "app_name",
|
||||
output: "output",
|
||||
templates: ["input"],
|
||||
verbose: 0,
|
||||
data: { customDate },
|
||||
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])
|
||||
}
|
||||
})
|
||||
|
||||
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"],
|
||||
verbose: 0,
|
||||
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])
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
@@ -381,7 +292,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name", "app_name.txt"))
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
})
|
||||
|
||||
test("should work with default helper", async () => {
|
||||
@@ -395,7 +306,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "APP_NAME", "app_name.txt"))
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
})
|
||||
|
||||
test("should work with custom helper", async () => {
|
||||
@@ -412,7 +323,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "REPLACED", "app_name.txt"))
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
})
|
||||
})
|
||||
)
|
||||
@@ -431,7 +342,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
})
|
||||
|
||||
test("should work with custom callback", async () => {
|
||||
@@ -448,7 +359,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
|
||||
expect(data.toString()).toEqual(
|
||||
expect(data.toString()).toBe(
|
||||
[
|
||||
"Hello, my app is app_name".toUpperCase(),
|
||||
fileStructNormal.input["{{name}}.txt"],
|
||||
@@ -456,21 +367,6 @@ describe("Scaffold", () => {
|
||||
].join(", ")
|
||||
)
|
||||
})
|
||||
test("should work with undefined response custom callback", async () => {
|
||||
await Scaffold({
|
||||
name: "app_name",
|
||||
output: "output",
|
||||
templates: ["input"],
|
||||
verbose: 0,
|
||||
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")
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { dateHelper, handlebarsParse, nowHelper } from "../src/utils"
|
||||
import { handlebarsParse } from "../src/utils"
|
||||
import { ScaffoldConfig } from "../src/types"
|
||||
import path from "path"
|
||||
import * as dateFns from "date-fns"
|
||||
|
||||
const blankConf: ScaffoldConfig = {
|
||||
verbose: 0,
|
||||
@@ -54,38 +53,4 @@ describe("Utils", () => {
|
||||
).toEqual(Buffer.from("/home/test/test {{escaped}}.txt"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("Helpers", () => {
|
||||
describe("date helpers", () => {
|
||||
describe("now", () => {
|
||||
test("should work without extra params", () => {
|
||||
const now = new Date()
|
||||
const fmt = "yyyy-MM-dd HH:mm"
|
||||
|
||||
expect(nowHelper(fmt)).toEqual(dateFns.format(now, fmt))
|
||||
})
|
||||
})
|
||||
|
||||
describe("date", () => {
|
||||
test("should work with no offset params", () => {
|
||||
const now = new Date()
|
||||
const fmt = "yyyy-MM-dd HH:mm"
|
||||
|
||||
expect(dateHelper(now.toISOString(), fmt)).toEqual(dateFns.format(now, fmt))
|
||||
})
|
||||
|
||||
test("should work with offset params", () => {
|
||||
const now = new Date()
|
||||
const fmt = "yyyy-MM-dd HH:mm"
|
||||
|
||||
expect(dateHelper(now.toISOString(), fmt, -1, "days")).toEqual(
|
||||
dateFns.format(dateFns.add(now, { days: -1 }), fmt)
|
||||
)
|
||||
expect(dateHelper(now.toISOString(), fmt, 1, "months")).toEqual(
|
||||
dateFns.format(dateFns.add(now, { months: 1 }), fmt)
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1050,11 +1050,6 @@ data-urls@^2.0.0:
|
||||
whatwg-mimetype "^2.3.0"
|
||||
whatwg-url "^8.0.0"
|
||||
|
||||
date-fns@^2.28.0:
|
||||
version "2.28.0"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2"
|
||||
integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||
|
||||
Reference in New Issue
Block a user