mirror of
https://github.com/chenasraf/simple-scaffold.git
synced 2026-05-18 01:29:09 +00:00
Compare commits
23 Commits
v2.0.0-pre
...
v1.1.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e175e6815 | ||
|
|
0742fc8771 | ||
|
|
0ecf07de18 | ||
|
|
51081cf2ee | ||
|
|
5a39c08a9f | ||
|
|
83e5724152 | ||
|
|
7455ac1ead | ||
|
|
f315373d18 | ||
|
|
38d557d450 | ||
|
|
a27389e262 | ||
|
|
ce9fededf0 | ||
|
|
4653428bda | ||
|
|
19b2203f30 | ||
|
|
3f103cd4c2 | ||
|
|
ec5503bc3e | ||
|
|
19ad60c3cb | ||
|
|
f04dd7e487 | ||
|
|
4c9a838ebe | ||
|
|
6700708e1d | ||
|
|
2727bd526c | ||
|
|
834a805794 | ||
|
|
ba9434f504 | ||
|
|
59353f4121 |
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug, needs-triage
|
||||
assignees: chenasraf
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Prepare templates:
|
||||
```
|
||||
template contents
|
||||
```
|
||||
2. Run with args/config:
|
||||
```
|
||||
npx simple-scaffold@latest -t ... -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. iOS]
|
||||
- Node.js: [e.g. 16.8]
|
||||
- Simple Scaffold Version [e.g. 1.0.4]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
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.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -2,7 +2,7 @@ name: Alpha Releases
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [alpha]
|
||||
branches: [develop]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
2
.github/workflows/pull_requests.yml
vendored
2
.github/workflows/pull_requests.yml
vendored
@@ -2,7 +2,7 @@ name: Pull Requests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master, alpha, beta]
|
||||
branches: [master, develop]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Releases
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -3,6 +3,7 @@
|
||||
"npm.packageManager": "yarn",
|
||||
"cSpell.words": [
|
||||
"massarg",
|
||||
"myname",
|
||||
"nobrace",
|
||||
"nocomment",
|
||||
"nodir",
|
||||
|
||||
133
README.md
133
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.
|
||||
|
||||
@@ -9,6 +9,38 @@ Simply organize your commonly-created files in their original structure, and run
|
||||
will copy the files to the output path, while replacing values (such as component or app name, or
|
||||
other custom data) inside the paths or contents of the files using Handlebars.js syntax.
|
||||
|
||||
<br />
|
||||
|
||||
<details>
|
||||
<summary>Table of contents</summary>
|
||||
|
||||
- [Simple Scaffold](#simple-scaffold)
|
||||
- [Install](#install)
|
||||
- [Use as a command line tool](#use-as-a-command-line-tool)
|
||||
- [Command Line Options](#command-line-options)
|
||||
- [Use in Node.js](#use-in-nodejs)
|
||||
- [Node-specific options](#node-specific-options)
|
||||
- [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)
|
||||
- [Examples](#examples)
|
||||
- [Command Example](#command-example)
|
||||
- [Example Scaffold Input](#example-scaffold-input)
|
||||
- [Input Directory structure](#input-directory-structure)
|
||||
- [Contents of `project/scaffold/{{Name}}.jsx`](#contents-of-projectscaffoldnamejsx)
|
||||
- [Example Scaffold Output](#example-scaffold-output)
|
||||
- [Output directory structure](#output-directory-structure)
|
||||
- [Contents of `project/scaffold/MyComponent/MyComponent.jsx`](#contents-of-projectscaffoldmycomponentmycomponentjsx)
|
||||
- [Contributing](#contributing)
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
## Install
|
||||
|
||||
You can either use it as a command line tool or import into your own code and run from there.
|
||||
@@ -104,30 +136,23 @@ const config = {
|
||||
},
|
||||
helpers: {
|
||||
twice: (text) => [text, text].join(" ")
|
||||
}
|
||||
},
|
||||
beforeWrite: (content, rawContent, outputPath) => content.toString().toUpperCase()
|
||||
}
|
||||
|
||||
const scaffold = Scaffold(config)
|
||||
```
|
||||
|
||||
### Additional Node.js options
|
||||
### Node-specific options
|
||||
|
||||
In addition to all the options available in the command line, there are some JS-specific options
|
||||
available:
|
||||
In addition to all the options available in the command line, there are some Node/JS-specific
|
||||
options available:
|
||||
|
||||
1. When `output` is used in Node directly, it may also be passed a function for each input file to
|
||||
output into a dynamic path:
|
||||
|
||||
```typescript
|
||||
config.output = (fullPath, baseDir, baseName) => {
|
||||
console.log({ fullPath, baseDir, baseName })
|
||||
return path.resolve(baseDir, baseName)
|
||||
}
|
||||
```
|
||||
|
||||
2. You may add custom `helpers` to your scaffolds. Helpers are simple `(string) => string` 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.
|
||||
| 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. 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
|
||||
|
||||
@@ -188,28 +213,58 @@ Your `data` will be pre-populated with the following:
|
||||
> [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.
|
||||
|
||||
Here are the built-in helpers available for use:
|
||||
#### 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 |
|
||||
| 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 |
|
||||
|
||||
> These helpers are available for any data property, not exclusive to `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
|
||||
@@ -221,8 +276,8 @@ config.helpers = {
|
||||
}
|
||||
```
|
||||
|
||||
These helpers will also be available to you when using `subFolderNameHelper` or
|
||||
`--sub-folder-name-helper` as a possible value.
|
||||
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.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -298,6 +353,16 @@ 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.
|
||||
|
||||
|
||||
16
package.json
16
package.json
@@ -1,13 +1,23 @@
|
||||
{
|
||||
"name": "simple-scaffold",
|
||||
"version": "1.0.4",
|
||||
"version": "1.1.0-alpha.5",
|
||||
"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": "cmd.js",
|
||||
"keywords": ["javascript", "cli", "template", "files", "typescript", "generator", "scaffold", "file", "scaffolding"],
|
||||
"keywords": [
|
||||
"javascript",
|
||||
"cli",
|
||||
"template",
|
||||
"files",
|
||||
"typescript",
|
||||
"generator",
|
||||
"scaffold",
|
||||
"file",
|
||||
"scaffolding"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist/",
|
||||
"build": "yarn clean && tsc && chmod -R +x ./dist && cp ./package.json ./README.md ./dist/",
|
||||
@@ -20,6 +30,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
"date-fns": "^2.28.0",
|
||||
"glob": "^7.1.3",
|
||||
"handlebars": "^4.7.7",
|
||||
"lodash": "^4.17.21",
|
||||
@@ -27,7 +38,6 @@
|
||||
"util.promisify": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/args": "^3.0.1",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/lodash": "^4.14.171",
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
makeRelativePath,
|
||||
registerHelpers,
|
||||
getTemplateGlobInfo,
|
||||
ensureFileExists,
|
||||
getFileList,
|
||||
getBasePath,
|
||||
copyFileTransformed,
|
||||
@@ -51,7 +50,7 @@ import { LogLevel, ScaffoldConfig } from "./types"
|
||||
* 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)
|
||||
*/
|
||||
export async function Scaffold({ ...options }: ScaffoldConfig) {
|
||||
export async function Scaffold({ ...options }: ScaffoldConfig): Promise<void> {
|
||||
options.output ??= process.cwd()
|
||||
|
||||
registerHelpers(options)
|
||||
@@ -64,7 +63,6 @@ export async function Scaffold({ ...options }: ScaffoldConfig) {
|
||||
options,
|
||||
_template
|
||||
)
|
||||
await ensureFileExists(template, isDirOrGlob)
|
||||
const files = await getFileList(options, template)
|
||||
for (const inputFilePath of files) {
|
||||
if (await isDir(inputFilePath)) {
|
||||
@@ -82,7 +80,10 @@ export async function Scaffold({ ...options }: ScaffoldConfig) {
|
||||
isDirOrGlob,
|
||||
isGlob,
|
||||
})
|
||||
await handleTemplateFile(options, options.data, { templatePath: inputFilePath, basePath })
|
||||
await handleTemplateFile(options, options.data, {
|
||||
templatePath: inputFilePath,
|
||||
basePath,
|
||||
})
|
||||
}
|
||||
} catch (e: any) {
|
||||
handleErr(e)
|
||||
@@ -104,7 +105,7 @@ async function handleTemplateFile(
|
||||
templatePath,
|
||||
basePath,
|
||||
})
|
||||
const overwrite = getOptionValueForFile(options, inputPath, data, options.overwrite ?? false)
|
||||
const overwrite = getOptionValueForFile(options, inputPath, options.overwrite ?? false)
|
||||
|
||||
log(
|
||||
options,
|
||||
@@ -121,7 +122,7 @@ async function handleTemplateFile(
|
||||
await createDirIfNotExists(path.dirname(outputPath), options)
|
||||
|
||||
log(options, LogLevel.Info, `Writing to ${outputPath}`)
|
||||
await copyFileTransformed(options, data, { exists, overwrite, outputPath, inputPath })
|
||||
await copyFileTransformed(options, { exists, overwrite, outputPath, inputPath })
|
||||
resolve()
|
||||
} catch (e: any) {
|
||||
handleErr(e)
|
||||
|
||||
32
src/types.ts
32
src/types.ts
@@ -12,17 +12,19 @@ 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 = (text: string) => string
|
||||
export type Helper = Handlebars.HelperDelegate
|
||||
|
||||
export interface ScaffoldConfig {
|
||||
/**
|
||||
@@ -113,6 +115,26 @@ export interface ScaffoldConfig {
|
||||
* helpers, or a custom one you provide to `helpers`. Defaults to `undefined`, which means no transformation is done.
|
||||
*/
|
||||
subFolderNameHelper?: DefaultHelperKeys | 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 `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>
|
||||
}
|
||||
export interface ScaffoldCmdConfig {
|
||||
name: string
|
||||
|
||||
118
src/utils.ts
118
src/utils.ts
@@ -9,6 +9,15 @@ 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"
|
||||
@@ -23,9 +32,53 @@ export const defaultHelpers: Record<DefaultHelperKeys, Helper> = {
|
||||
pascalCase,
|
||||
lowerCase: (text) => text.toLowerCase(),
|
||||
upperCase: (text) => text.toUpperCase(),
|
||||
now: nowHelper,
|
||||
date: dateHelper,
|
||||
}
|
||||
|
||||
export function registerHelpers(options: ScaffoldConfig) {
|
||||
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 {
|
||||
const _helpers = { ...defaultHelpers, ...options.helpers }
|
||||
for (const helperName in _helpers) {
|
||||
log(options, LogLevel.Debug, `Registering helper: ${helperName}`)
|
||||
@@ -33,11 +86,11 @@ export function registerHelpers(options: ScaffoldConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
export function handleErr(err: NodeJS.ErrnoException | null) {
|
||||
export function handleErr(err: NodeJS.ErrnoException | null): void {
|
||||
if (err) throw err
|
||||
}
|
||||
|
||||
export function log(options: ScaffoldConfig, level: LogLevel, ...obj: any[]) {
|
||||
export function log(options: ScaffoldConfig, level: LogLevel, ...obj: any[]): void {
|
||||
if (options.quiet || options.verbose === LogLevel.None || level < (options.verbose ?? LogLevel.Info)) {
|
||||
return
|
||||
}
|
||||
@@ -86,7 +139,6 @@ export async function createDirIfNotExists(dir: string, options: ScaffoldConfig)
|
||||
export function getOptionValueForFile<T>(
|
||||
options: ScaffoldConfig,
|
||||
filePath: string,
|
||||
data: Record<string, string>,
|
||||
fn: FileResponse<T>,
|
||||
defaultValue?: T
|
||||
): T {
|
||||
@@ -104,7 +156,7 @@ export function handlebarsParse(
|
||||
options: ScaffoldConfig,
|
||||
templateBuffer: Buffer | string,
|
||||
{ isPath = false }: { isPath?: boolean } = {}
|
||||
) {
|
||||
): Buffer {
|
||||
const { data } = options
|
||||
try {
|
||||
let str = templateBuffer.toString()
|
||||
@@ -116,11 +168,11 @@ export function handlebarsParse(
|
||||
if (isPath && path.sep !== "/") {
|
||||
outputContents = outputContents.replace(/\//g, "\\")
|
||||
}
|
||||
return outputContents
|
||||
return Buffer.from(outputContents)
|
||||
} catch (e) {
|
||||
log(options, LogLevel.Debug, e)
|
||||
log(options, LogLevel.Warning, "Couldn't parse file with handlebars, returning original content")
|
||||
return templateBuffer
|
||||
return Buffer.from(templateBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +197,7 @@ export async function isDir(path: string): Promise<boolean> {
|
||||
return tplStat.isDirectory()
|
||||
}
|
||||
|
||||
export function removeGlob(template: string) {
|
||||
export function removeGlob(template: string): string {
|
||||
return template.replace(/\*/g, "").replace(/(\/\/|\\\\)/g, path.sep)
|
||||
}
|
||||
|
||||
@@ -153,14 +205,14 @@ export function makeRelativePath(str: string): string {
|
||||
return str.startsWith(path.sep) ? str.slice(1) : str
|
||||
}
|
||||
|
||||
export function getBasePath(relPath: string) {
|
||||
export function getBasePath(relPath: string): string {
|
||||
return path
|
||||
.resolve(process.cwd(), relPath)
|
||||
.replace(process.cwd() + path.sep, "")
|
||||
.replace(process.cwd(), "")
|
||||
}
|
||||
|
||||
export async function getFileList(options: ScaffoldConfig, template: string) {
|
||||
export async function getFileList(options: ScaffoldConfig, template: string): Promise<string[]> {
|
||||
return (
|
||||
await promisify(glob)(template, {
|
||||
dot: true,
|
||||
@@ -193,16 +245,6 @@ export async function getTemplateGlobInfo(options: ScaffoldConfig, template: str
|
||||
return { nonGlobTemplate, origTemplate, isDirOrGlob, isGlob, template: _template }
|
||||
}
|
||||
|
||||
export async function ensureFileExists(template: string, isGlob: boolean) {
|
||||
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
|
||||
@@ -217,8 +259,8 @@ export async function getTemplateFileInfo(
|
||||
{ templatePath, basePath }: { templatePath: string; basePath: string }
|
||||
): Promise<OutputFileInfo> {
|
||||
const inputPath = path.resolve(process.cwd(), templatePath)
|
||||
const outputPathOpt = getOptionValueForFile(options, inputPath, data, options.output)
|
||||
const outputDir = getOutputDir(options, data, outputPathOpt, basePath)
|
||||
const outputPathOpt = getOptionValueForFile(options, inputPath, options.output)
|
||||
const outputDir = getOutputDir(options, outputPathOpt, basePath)
|
||||
const outputPath = handlebarsParse(options, path.join(outputDir, path.basename(inputPath)), {
|
||||
isPath: true,
|
||||
}).toString()
|
||||
@@ -228,39 +270,41 @@ export async function getTemplateFileInfo(
|
||||
|
||||
export async function copyFileTransformed(
|
||||
options: ScaffoldConfig,
|
||||
data: Record<string, string>,
|
||||
{
|
||||
exists,
|
||||
overwrite,
|
||||
outputPath,
|
||||
inputPath,
|
||||
}: { exists: boolean; overwrite: boolean; outputPath: string; inputPath: string }
|
||||
) {
|
||||
}: {
|
||||
exists: boolean
|
||||
overwrite: boolean
|
||||
outputPath: string
|
||||
inputPath: string
|
||||
}
|
||||
): Promise<void> {
|
||||
if (!exists || overwrite) {
|
||||
if (exists && overwrite) {
|
||||
log(options, LogLevel.Info, `File ${outputPath} exists, overwriting`)
|
||||
}
|
||||
const templateBuffer = await readFile(inputPath)
|
||||
const outputContents = handlebarsParse(options, templateBuffer)
|
||||
const unprocessedOutputContents = handlebarsParse(options, templateBuffer)
|
||||
const finalOutputContents = (
|
||||
(await options.beforeWrite?.(unprocessedOutputContents, templateBuffer, outputPath)) ?? unprocessedOutputContents
|
||||
).toString()
|
||||
|
||||
if (!options.dryRun) {
|
||||
await writeFile(outputPath, outputContents)
|
||||
await writeFile(outputPath, finalOutputContents)
|
||||
log(options, LogLevel.Info, "Done.")
|
||||
} else {
|
||||
log(options, LogLevel.Info, "Content output:")
|
||||
log(options, LogLevel.Info, outputContents)
|
||||
log(options, LogLevel.Info, finalOutputContents)
|
||||
}
|
||||
} else if (exists) {
|
||||
log(options, LogLevel.Info, `File ${outputPath} already exists, skipping`)
|
||||
}
|
||||
}
|
||||
|
||||
export function getOutputDir(
|
||||
options: ScaffoldConfig,
|
||||
data: Record<string, string>,
|
||||
outputPathOpt: string,
|
||||
basePath: string
|
||||
) {
|
||||
export function getOutputDir(options: ScaffoldConfig, outputPathOpt: string, basePath: string): string {
|
||||
return path.resolve(
|
||||
process.cwd(),
|
||||
...([
|
||||
@@ -268,7 +312,7 @@ export function getOutputDir(
|
||||
basePath,
|
||||
options.createSubFolder
|
||||
? options.subFolderNameHelper
|
||||
? handlebarsParse(options, `{{ ${options.subFolderNameHelper} name }}`)
|
||||
? handlebarsParse(options, `{{ ${options.subFolderNameHelper} name }}`).toString()
|
||||
: options.name
|
||||
: undefined,
|
||||
].filter(Boolean) as string[])
|
||||
@@ -296,7 +340,7 @@ export function logInputFile(
|
||||
isDirOrGlob: boolean
|
||||
isGlob: boolean
|
||||
}
|
||||
) {
|
||||
): void {
|
||||
log(
|
||||
options,
|
||||
LogLevel.Debug,
|
||||
@@ -313,7 +357,7 @@ export function logInputFile(
|
||||
)
|
||||
}
|
||||
|
||||
export function logInitStep(options: ScaffoldConfig) {
|
||||
export function logInitStep(options: ScaffoldConfig): void {
|
||||
log(options, LogLevel.Debug, "Full config:", {
|
||||
name: options.name,
|
||||
templates: options.templates,
|
||||
|
||||
@@ -5,6 +5,7 @@ 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: {
|
||||
@@ -52,8 +53,16 @@ const fileStructHelpers = {
|
||||
},
|
||||
output: {},
|
||||
}
|
||||
// let logsTemp: any = []
|
||||
// let logMock: any
|
||||
|
||||
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(() => {
|
||||
@@ -85,7 +94,7 @@ describe("Scaffold", () => {
|
||||
verbose: 0,
|
||||
})
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
})
|
||||
|
||||
test("should create with config", async () => {
|
||||
@@ -98,7 +107,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name", "app_name.txt"))
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
})
|
||||
})
|
||||
)
|
||||
@@ -124,7 +133,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
|
||||
expect(data.toString()).toBe("Hello, my value is 1")
|
||||
expect(data.toString()).toEqual("Hello, my value is 1")
|
||||
})
|
||||
|
||||
test("should overwrite with config", async () => {
|
||||
@@ -146,7 +155,7 @@ describe("Scaffold", () => {
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name.txt"))
|
||||
expect(data.toString()).toBe("Hello, my value is 2")
|
||||
expect(data.toString()).toEqual("Hello, my value is 2")
|
||||
})
|
||||
})
|
||||
)
|
||||
@@ -174,6 +183,43 @@ 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()
|
||||
})
|
||||
})
|
||||
@@ -191,7 +237,7 @@ describe("Scaffold", () => {
|
||||
verbose: 0,
|
||||
})
|
||||
const data = readFileSync(join(process.cwd(), "/custom-output/app_name/app_name.txt"))
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
expect(data.toString()).toEqual("Hello, my app is app_name")
|
||||
})
|
||||
})
|
||||
)
|
||||
@@ -226,56 +272,99 @@ describe("Scaffold", () => {
|
||||
)
|
||||
|
||||
describe(
|
||||
"helpers",
|
||||
"capitalization helpers",
|
||||
withMock(fileStructHelpers, () => {
|
||||
const _helpers: Record<string, (text: string) => string> = {
|
||||
add1: (text) => text + " 1",
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
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])
|
||||
}
|
||||
})
|
||||
describe("custom helpers", () => {
|
||||
test("should work", async () => {
|
||||
await Scaffold({
|
||||
name: "app_name",
|
||||
output: "output",
|
||||
templates: ["input"],
|
||||
verbose: 0,
|
||||
helpers: _helpers,
|
||||
})
|
||||
})
|
||||
)
|
||||
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 }))
|
||||
|
||||
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])
|
||||
}
|
||||
await Scaffold({
|
||||
name: "app_name",
|
||||
output: "output",
|
||||
templates: ["input"],
|
||||
verbose: 0,
|
||||
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"],
|
||||
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])
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
@@ -291,8 +380,8 @@ describe("Scaffold", () => {
|
||||
verbose: 0,
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "app_name/app_name.txt"))
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
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 () => {
|
||||
@@ -305,8 +394,8 @@ describe("Scaffold", () => {
|
||||
subFolderNameHelper: "upperCase",
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "APP_NAME/app_name.txt"))
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
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 () => {
|
||||
@@ -322,8 +411,65 @@ describe("Scaffold", () => {
|
||||
},
|
||||
})
|
||||
|
||||
const data = readFileSync(join(process.cwd(), "output", "REPLACED/app_name.txt"))
|
||||
expect(data.toString()).toBe("Hello, my app is app_name")
|
||||
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"],
|
||||
verbose: 0,
|
||||
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"],
|
||||
verbose: 0,
|
||||
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"],
|
||||
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,6 +1,7 @@
|
||||
import { handlebarsParse } from "../src/utils"
|
||||
import { dateHelper, handlebarsParse, nowHelper } from "../src/utils"
|
||||
import { ScaffoldConfig } from "../src/types"
|
||||
import path from "path"
|
||||
import * as dateFns from "date-fns"
|
||||
|
||||
const blankConf: ScaffoldConfig = {
|
||||
verbose: 0,
|
||||
@@ -23,7 +24,7 @@ describe("Utils", () => {
|
||||
})
|
||||
test("should work for windows paths", async () => {
|
||||
expect(handlebarsParse(blankConf, "C:\\exports\\{{name}}.txt", { isPath: true })).toEqual(
|
||||
"C:\\exports\\test.txt"
|
||||
Buffer.from("C:\\exports\\test.txt")
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -36,7 +37,9 @@ describe("Utils", () => {
|
||||
Object.defineProperty(path, "sep", { value: origSep })
|
||||
})
|
||||
test("should work for non-windows paths", async () => {
|
||||
expect(handlebarsParse(blankConf, "/home/test/{{name}}.txt", { isPath: true })).toEqual("/home/test/test.txt")
|
||||
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 () => {
|
||||
@@ -48,7 +51,41 @@ describe("Utils", () => {
|
||||
isPath: false,
|
||||
}
|
||||
)
|
||||
).toEqual("/home/test/test {{escaped}}.txt")
|
||||
).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)
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@@ -537,11 +537,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1"
|
||||
integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==
|
||||
|
||||
"@types/args@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/args/-/args-3.0.1.tgz#59e8e787ed8a810408dd9a324157cc20b7001468"
|
||||
integrity sha512-Mfre8T2comeJbw2D6W8mzQP+0Q8fpS7nkbHgatzU31tWsLs0Lkyc+ObdYfgV4SuMZn/n5MEWlxh2rc25125s0Q==
|
||||
|
||||
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14":
|
||||
version "7.1.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024"
|
||||
@@ -1055,6 +1050,11 @@ 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