Compare commits

...

2 Commits

Author SHA1 Message Date
Chen Asraf
5d630e7e62 added custom helpers 2021-12-06 00:00:18 +02:00
Chen Asraf
7073add836 update deps + add MIGRATION.md 2021-12-05 23:28:23 +02:00
8 changed files with 162 additions and 19 deletions

20
MIGRATION.md Normal file
View File

@@ -0,0 +1,20 @@
# Migrating from 0.x to 1.0
In Simple Scaffold v1.0, the entire codebase was overhauled, yet usage remains mostly the same
between versions. With these notable exceptions:
- Some of the argument names have changed
- Template syntax has been improved
## Argument changes
- `locals` has been renamed to `data`. The appropriate command line args have been updated as
well to `--data` | `-d`.
## Template syntax changes
Simple Scaffold still uses Handlebars.js to handle template content and file names. However, helpers
have been added to remove the need for you to pre-process the template data on simple use-cases such
as case type manipulation (converting to camel case, snake case, etc)
See the readme for the full information on how to use these helpers and which are available.

View File

@@ -102,18 +102,30 @@ const scaffold = SimpleScaffold({
locals: {
property: "value",
},
helpers: {
twice: (text) => [text, text].join(" ")
}
})
```
The exception in the config is that `output`, when used in Node directly, may also be passed a
function for each input file to output into a dynamic path:
### Additional Node.js options
```typescript
config.output = (fullPath, baseDir, baseName) => {
console.log({ fullPath, baseDir, baseName })
return path.resolve(baseDir, baseName)
}
```
In addition to all the options available in the command line, there are some 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.
## Preparing files
@@ -156,9 +168,21 @@ Here are the built-in helpers available for use:
| kebabCase | `{{ kebabCase name }}` | my-name |
| hyphenCase | `{{ hyphenCase name }}` | my-name |
| pascalCase | `{{ pascalCase name }}` | MyName |
| upperCase | `{{ upperCase name }}` | MYNAME |
| lowerCase | `{{ lowerCase name }}` | myname |
> 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
the transformation functions. For example, `upperCase` is implemented like so:
```typescript
config.helpers = {
upperCase: (text) => text.toUpperCase(),
}
```
## Examples
### Command Example

View File

@@ -1,6 +1,6 @@
{
"name": "simple-scaffold",
"version": "1.0.0-alpha.12",
"version": "1.0.0-alpha.13",
"description": "Create files based on templates",
"repository": "https://github.com/chenasraf/simple-scaffold.git",
"author": "Chen Asraf <inbox@casraf.com>",
@@ -23,7 +23,7 @@
"glob": "^7.1.3",
"handlebars": "^4.7.7",
"lodash": "^4.17.21",
"massarg": "^1.0.4",
"massarg": "^1.0.5",
"util.promisify": "^1.1.1"
},
"devDependencies": {

View File

@@ -15,6 +15,7 @@ import {
isDir,
removeGlob,
makeRelativePath,
registerHelpers,
} from "./utils"
import { LogLevel, ScaffoldConfig } from "./types"
@@ -30,6 +31,7 @@ export async function Scaffold(config: ScaffoldConfig) {
data: options.data,
overwrite: options.overwrite,
quiet: options.quiet,
helpers: Object.keys(options.helpers ?? {}),
verbose: `${options.verbose} (${Object.keys(LogLevel).find(
(k) => (LogLevel[k as any] as unknown as number) === options.verbose!
)})`,
@@ -37,6 +39,7 @@ export async function Scaffold(config: ScaffoldConfig) {
log(options, LogLevel.Info, "Data:", data)
for (let template of config.templates) {
try {
registerHelpers(options)
const _isGlob = template.includes("*")
if (!_isGlob && !(await pathExists(template))) {
const err: NodeJS.ErrnoException = new Error(`ENOENT, no such file or directory ${template}`)

View File

@@ -61,6 +61,26 @@ export interface ScaffoldConfig {
* actual file contents or create directories. (default: `false`)
*/
dryRun?: boolean
/**
* Additional helpers to add to the template parser. Provide an object whose keys are the name of the function to add,
* and the value is the helper function itself. The signature of helpers is as follows:
* ```typescript
* (text: string) => string
* ```
*
* A full example might be:
*
* ```typescript
* Scaffold({
* //...
* helpers: {
* upperCamelCase: (text) => camelCase(text).toUpperCase()
* }
* })
* ```
*/
helpers?: Record<string, (text: string) => string>
}
export interface ScaffoldCmdConfig {
name: string

View File

@@ -10,17 +10,23 @@ import { promises as fsPromises } from "fs"
import chalk from "chalk"
const { stat, access, mkdir } = fsPromises
const helpers = {
export const defaultHelpers: Exclude<ScaffoldConfig["helpers"], undefined> = {
camelCase,
snakeCase,
startCase,
kebabCase,
hyphenCase: kebabCase,
pascalCase,
lowerCase: (text) => text.toLowerCase(),
upperCase: (text) => text.toUpperCase(),
}
for (const helperName in helpers) {
Handlebars.registerHelper(helperName, helpers[helperName as keyof typeof helpers])
export function registerHelpers(options: ScaffoldConfig) {
const _helpers = { ...defaultHelpers, ...options.helpers }
for (const helperName in _helpers) {
log(options, LogLevel.Debug, `Registering helper: ${helperName}`)
Handlebars.registerHelper(helperName, _helpers[helperName as keyof typeof _helpers])
}
}
export function handleErr(err: NodeJS.ErrnoException | null) {

View File

@@ -3,6 +3,7 @@ import FileSystem from "mock-fs/lib/filesystem"
import Scaffold from "../src/scaffold"
import { readdirSync, readFileSync } from "fs"
import { Console } from "console"
import { defaultHelpers } from "../src/utils"
const fileStructNormal = {
input: {
@@ -30,6 +31,20 @@ const fileStructNested = {
},
output: {},
}
const defaultHelperNames = Object.keys(defaultHelpers)
const fileStructHelpers = {
input: {
defaults: defaultHelperNames.reduce<Record<string, string>>(
(all, cur) => ({ ...all, [cur + ".txt"]: `{{ ${cur} name }}` }),
{}
),
custom: {
"add1.txt": "{{ add1 name }}",
},
},
output: {},
}
// let logsTemp: any = []
// let logMock: any
function withMock(fileStruct: FileSystem.DirectoryItems, testFn: jest.EmptyFunction): jest.EmptyFunction {
@@ -202,4 +217,59 @@ describe("Scaffold", () => {
})
})
)
describe(
"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(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,
})
const results = {
add1: "app_name 1",
}
for (const key in results) {
const file = readFileSync(process.cwd() + `/output/custom/${key}.txt`)
expect(file.toString()).toEqual(results[key as keyof typeof results])
}
})
})
})
)
})

View File

@@ -932,7 +932,7 @@ chalk@2.4.2, chalk@^2.0.0:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^4.0.0, chalk@^4.1.1:
chalk@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
@@ -2161,12 +2161,12 @@ makeerror@1.0.x:
dependencies:
tmpl "1.0.x"
massarg@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/massarg/-/massarg-1.0.4.tgz#8756193bee9bc7331b1f48a8840e86acf808f980"
integrity sha512-SakImNzZP8SN6tlcGBoC62z+12yv/To+h7KP8XapnOux5Mjiwprn4KhNHsjh2rbrKCAPqZQWYziAitSzGTkjMw==
massarg@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/massarg/-/massarg-1.0.5.tgz#3dfd49bb63bfca4b0371a2f0ffc7580a9a6cc7a0"
integrity sha512-gkJHZsNfeMurpVPKojCiT2lnG2cSxHPAXZSg+gCLIgCf5bGn7pKOsoeu4qVAc5mlBxV2l/hA1yojt+ZsJd8SDg==
dependencies:
chalk "^4.1.1"
chalk "^4.1.2"
lodash "^4.17.21"
merge-stream@^2.0.0: