Compare commits

..

13 Commits

Author SHA1 Message Date
Chen Asraf
ce9fededf0 update workflows + release alpha 2022-04-19 17:05:57 +03:00
Chen Asraf
4653428bda Merge pull request #33 from chenasraf/feature/before-write-callback
beforeWrite callback
2022-04-19 17:02:04 +03:00
Chen Asraf
19b2203f30 correct version number 2022-04-19 16:59:15 +03:00
Chen Asraf
3f103cd4c2 docs: add table of contents 2022-04-19 16:55:16 +03:00
Chen Asraf
ec5503bc3e bump version number 2022-04-19 16:49:57 +03:00
Chen Asraf
19ad60c3cb add explicit function return types 2022-04-19 16:43:57 +03:00
Chen Asraf
f04dd7e487 clean up + update docs 2022-04-19 16:43:57 +03:00
Chen Asraf
4c9a838ebe implementation of before-write callback + tests 2022-04-19 16:43:57 +03:00
Chen Asraf
6700708e1d update PR workflow 2022-04-19 16:43:11 +03:00
Chen Asraf
2727bd526c Merge pull request #29 from chenasraf/github/issue-templates
Update issue templates [skip ci]
2022-04-19 15:50:13 +03:00
Chen Asraf
834a805794 Update issue templates [skip ci] 2022-04-13 18:08:45 +03:00
Chen Asraf
ba9434f504 chore: remove unneeded dep 2022-04-13 10:21:46 +03:00
Chen Asraf
59353f4121 add develop branch 2022-04-13 10:19:55 +03:00
9 changed files with 120 additions and 352 deletions

View File

@@ -3,7 +3,6 @@
"npm.packageManager": "yarn",
"cSpell.words": [
"massarg",
"myname",
"nobrace",
"nocomment",
"nodir",

113
README.md
View File

@@ -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.

View File

@@ -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"
}
}
}

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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) {

View File

@@ -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")
})
})
)
})

View File

@@ -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)
)
})
})
})
})
})

View File

@@ -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"