Skip to content

Commit d03d0e0

Browse files
committed
added custom helpers
1 parent 5b72b6c commit d03d0e0

6 files changed

Lines changed: 135 additions & 12 deletions

File tree

README.md

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,30 @@ const scaffold = SimpleScaffold({
102102
locals: {
103103
property: "value",
104104
},
105+
helpers: {
106+
twice: (text) => [text, text].join(" ")
107+
}
105108
})
106109
```
107110

108-
The exception in the config is that `output`, when used in Node directly, may also be passed a
109-
function for each input file to output into a dynamic path:
111+
### Additional Node.js options
110112

111-
```typescript
112-
config.output = (fullPath, baseDir, baseName) => {
113-
console.log({ fullPath, baseDir, baseName })
114-
return path.resolve(baseDir, baseName)
115-
}
116-
```
113+
In addition to all the options available in the command line, there are some JS-specific options
114+
available:
115+
116+
1. When `output` is used in Node directly, it may also be passed a function for each input file to
117+
output into a dynamic path:
118+
119+
```typescript
120+
config.output = (fullPath, baseDir, baseName) => {
121+
console.log({ fullPath, baseDir, baseName })
122+
return path.resolve(baseDir, baseName)
123+
}
124+
```
125+
126+
2. You may add custom `helpers` to your scaffolds. Helpers are simple `(string) => string` functions
127+
that transform your `data` variables into other values. See [Helpers](#helpers) for the list of
128+
default helpers, or add your own to be loaded into the template parser.
117129

118130
## Preparing files
119131

@@ -156,9 +168,21 @@ Here are the built-in helpers available for use:
156168
| kebabCase | `{{ kebabCase name }}` | my-name |
157169
| hyphenCase | `{{ hyphenCase name }}` | my-name |
158170
| pascalCase | `{{ pascalCase name }}` | MyName |
171+
| upperCase | `{{ upperCase name }}` | MYNAME |
172+
| lowerCase | `{{ lowerCase name }}` | myname |
159173

160174
> These helpers are available for any data property, not exclusive to `name`.
161175

176+
You may also add your own custom helpers using the `helpers` options when using the JS API (rather
177+
than the CLI). The `helpers` option takes an object whose keys are helper names, and values are
178+
the transformation functions. For example, `upperCase` is implemented like so:
179+
180+
```typescript
181+
config.helpers = {
182+
upperCase: (text) => text.toUpperCase(),
183+
}
184+
```
185+
162186
## Examples
163187

164188
### Command Example

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "simple-scaffold",
3-
"version": "1.0.0-alpha.12",
3+
"version": "1.0.0-alpha.13",
44
"description": "Create files based on templates",
55
"repository": "https://github.com/chenasraf/simple-scaffold.git",
66
"author": "Chen Asraf <inbox@casraf.com>",

src/scaffold.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
isDir,
1616
removeGlob,
1717
makeRelativePath,
18+
registerHelpers,
1819
} from "./utils"
1920
import { LogLevel, ScaffoldConfig } from "./types"
2021

@@ -30,13 +31,15 @@ export async function Scaffold(config: ScaffoldConfig) {
3031
data: options.data,
3132
overwrite: options.overwrite,
3233
quiet: options.quiet,
34+
helpers: Object.keys(options.helpers ?? {}),
3335
verbose: `${options.verbose} (${Object.keys(LogLevel).find(
3436
(k) => (LogLevel[k as any] as unknown as number) === options.verbose!
3537
)})`,
3638
})
3739
log(options, LogLevel.Info, "Data:", data)
3840
for (let template of config.templates) {
3941
try {
42+
registerHelpers(options)
4043
const _isGlob = template.includes("*")
4144
if (!_isGlob && !(await pathExists(template))) {
4245
const err: NodeJS.ErrnoException = new Error(`ENOENT, no such file or directory ${template}`)

src/types.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,26 @@ export interface ScaffoldConfig {
6161
* actual file contents or create directories. (default: `false`)
6262
*/
6363
dryRun?: boolean
64+
65+
/**
66+
* Additional helpers to add to the template parser. Provide an object whose keys are the name of the function to add,
67+
* and the value is the helper function itself. The signature of helpers is as follows:
68+
* ```typescript
69+
* (text: string) => string
70+
* ```
71+
*
72+
* A full example might be:
73+
*
74+
* ```typescript
75+
* Scaffold({
76+
* //...
77+
* helpers: {
78+
* upperCamelCase: (text) => camelCase(text).toUpperCase()
79+
* }
80+
* })
81+
* ```
82+
*/
83+
helpers?: Record<string, (text: string) => string>
6484
}
6585
export interface ScaffoldCmdConfig {
6686
name: string

src/utils.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,23 @@ import { promises as fsPromises } from "fs"
1010
import chalk from "chalk"
1111
const { stat, access, mkdir } = fsPromises
1212

13-
const helpers = {
13+
export const defaultHelpers: Exclude<ScaffoldConfig["helpers"], undefined> = {
1414
camelCase,
1515
snakeCase,
1616
startCase,
1717
kebabCase,
1818
hyphenCase: kebabCase,
1919
pascalCase,
20+
lowerCase: (text) => text.toLowerCase(),
21+
upperCase: (text) => text.toUpperCase(),
2022
}
2123

22-
for (const helperName in helpers) {
23-
Handlebars.registerHelper(helperName, helpers[helperName as keyof typeof helpers])
24+
export function registerHelpers(options: ScaffoldConfig) {
25+
const _helpers = { ...defaultHelpers, ...options.helpers }
26+
for (const helperName in _helpers) {
27+
log(options, LogLevel.Debug, `Registering helper: ${helperName}`)
28+
Handlebars.registerHelper(helperName, _helpers[helperName as keyof typeof _helpers])
29+
}
2430
}
2531

2632
export function handleErr(err: NodeJS.ErrnoException | null) {

tests/scaffold.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import FileSystem from "mock-fs/lib/filesystem"
33
import Scaffold from "../src/scaffold"
44
import { readdirSync, readFileSync } from "fs"
55
import { Console } from "console"
6+
import { defaultHelpers } from "../src/utils"
67

78
const fileStructNormal = {
89
input: {
@@ -30,6 +31,20 @@ const fileStructNested = {
3031
},
3132
output: {},
3233
}
34+
35+
const defaultHelperNames = Object.keys(defaultHelpers)
36+
const fileStructHelpers = {
37+
input: {
38+
defaults: defaultHelperNames.reduce<Record<string, string>>(
39+
(all, cur) => ({ ...all, [cur + ".txt"]: `{{ ${cur} name }}` }),
40+
{}
41+
),
42+
custom: {
43+
"add1.txt": "{{ add1 name }}",
44+
},
45+
},
46+
output: {},
47+
}
3348
// let logsTemp: any = []
3449
// let logMock: any
3550
function withMock(fileStruct: FileSystem.DirectoryItems, testFn: jest.EmptyFunction): jest.EmptyFunction {
@@ -202,4 +217,59 @@ describe("Scaffold", () => {
202217
})
203218
})
204219
)
220+
221+
describe(
222+
"helpers",
223+
withMock(fileStructHelpers, () => {
224+
const _helpers: Record<string, (text: string) => string> = {
225+
add1: (text) => text + " 1",
226+
}
227+
228+
describe("default helpers", () => {
229+
test("should work", async () => {
230+
await Scaffold({
231+
name: "app_name",
232+
output: "output",
233+
templates: ["input"],
234+
verbose: 0,
235+
helpers: _helpers,
236+
})
237+
238+
const results = {
239+
camelCase: "appName",
240+
snakeCase: "app_name",
241+
startCase: "App Name",
242+
kebabCase: "app-name",
243+
hyphenCase: "app-name",
244+
pascalCase: "AppName",
245+
lowerCase: "app_name",
246+
upperCase: "APP_NAME",
247+
}
248+
for (const key in results) {
249+
const file = readFileSync(process.cwd() + `/output/defaults/${key}.txt`)
250+
expect(file.toString()).toEqual(results[key as keyof typeof results])
251+
}
252+
})
253+
})
254+
describe("custom helpers", () => {
255+
test("should work", async () => {
256+
await Scaffold({
257+
name: "app_name",
258+
output: "output",
259+
templates: ["input"],
260+
verbose: 0,
261+
helpers: _helpers,
262+
})
263+
264+
const results = {
265+
add1: "app_name 1",
266+
}
267+
for (const key in results) {
268+
const file = readFileSync(process.cwd() + `/output/custom/${key}.txt`)
269+
expect(file.toString()).toEqual(results[key as keyof typeof results])
270+
}
271+
})
272+
})
273+
})
274+
)
205275
})

0 commit comments

Comments
 (0)