Compare commits

..

22 Commits

Author SHA1 Message Date
Chen Asraf
381c55835f fix basename in some cases 2021-12-03 00:39:03 +02:00
Chen Asraf
efdb7dc00b run tests on ci [skip publish] 2021-12-02 11:48:57 +02:00
Chen Asraf
385829aa27 fix errors, fix nested output 2021-12-02 11:46:08 +02:00
Chen Asraf
33c357bccc remove types from package.json 2021-11-28 15:50:27 +02:00
Chen Asraf
b74b781a5b fix yarn.lock 2021-11-28 15:45:51 +02:00
Chen Asraf
57410c8d74 fix log level 0 2021-11-28 15:34:59 +02:00
Chen Asraf
f81cfd8ae1 add export for cmd_util 2021-11-28 15:28:48 +02:00
Chen Asraf
414494734d bump version number 2021-11-28 14:24:01 +02:00
Chen Asraf
89b588f64e update workflows [skip publish] 2021-11-28 14:09:25 +02:00
Chen Asraf
cf22e2e62f fixes + add log level [skip publish] 2021-11-28 14:07:25 +02:00
Chen Asraf
246c139061 fix readme [skip publish] 2021-11-18 15:25:43 +02:00
Chen Asraf
711d8f0333 try fix release upload 2021-11-18 14:22:23 +02:00
Chen Asraf
8b22e96329 try new release version 2021-11-18 14:19:50 +02:00
Chen Asraf
53c0842ab8 fixed release tarball file location 2021-11-18 14:18:18 +02:00
Chen Asraf
48631325c1 fixed release tarball file location 2021-11-18 14:16:58 +02:00
Chen Asraf
045ad0118a fix build output files 2021-11-18 14:09:21 +02:00
Chen Asraf
b4dca7a954 add build step 2021-11-18 13:57:57 +02:00
Chen Asraf
7c42808f63 try to fix workflow 2021-11-18 13:56:24 +02:00
Chen Asraf
fd42013e8b publish: debug mode off, try to fix workflow 2021-11-18 13:55:30 +02:00
Chen Asraf
961a72fcdc publish: debug mode on 2021-11-18 13:51:29 +02:00
Chen Asraf
d6d99cfdf2 try fix workflow 2021-11-18 13:45:37 +02:00
Chen Asraf
ea4ecabe02 Merge pull request #12 from chenasraf/v1.0
v1.0
2021-11-18 13:44:09 +02:00
12 changed files with 177 additions and 413 deletions

View File

@@ -36,7 +36,6 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
prerelease: true
tag_name: ${{ steps.update_tag.outputs.tagname }}
release_name: Release ${{ steps.update_tag.outputs.tagname }}
- name: Upload Release Asset
@@ -48,5 +47,5 @@ jobs:
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./package.tgz
asset_name: simple-scaffold ${{ steps.update_tag.outputs.tagname }}.tgz
asset_name: package.tgz
asset_content_type: application/tgz

View File

@@ -46,5 +46,5 @@ jobs:
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./package.tgz
asset_name: simple-scaffold ${{ steps.update_tag.outputs.tagname }}.tgz
asset_name: package.tgz
asset_content_type: application/tgz

View File

@@ -1,20 +0,0 @@
# 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.

111
README.md
View File

@@ -2,16 +2,6 @@
Simple Scaffold allows you to create your structured files based on templates.
Simply organize your commonly-created files in their original structure, and replace any variable
values (such as component or app name) inside the paths or contents of the files with tokens to be
populated upon scaffolding.
Then, run Simple Scaffold and it will generate your files for you in the desired structure,
with file names and contents that contain your dynamic information.
It's a simple way to easily create reusable components, common class files to start writing from,
or even entire app structures.
## Install
You can either use it as a command line tool or import into your own code and run from there.
@@ -47,7 +37,8 @@ Options:
--templates|-t Template files to use as input. You may provide multiple
files, each of which can be a relative or absolute path, or a glob
pattern for multiple file matching easily. (default: current dir)
pattern for multiple file matching easily. (default:
)
--overwrite|-w Enable to override output files, even if they already exist.
(default: false)
@@ -87,13 +78,13 @@ 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.
Simply pass a config object to the constructor, and invoke `run()` when you are ready to start.
The config takes similar arguments to the command line:
```typescript
import Scaffold from "simple-scaffold"
```javascript
const SimpleScaffold = require("simple-scaffold").default
const config = {
const scaffold = SimpleScaffold({
name: "component",
templates: [path.join(__dirname, "scaffolds", "component")],
output: path.join(__dirname, "src", "components"),
@@ -101,32 +92,18 @@ const config = {
locals: {
property: "value",
},
helpers: {
twice: (text) => [text, text].join(" ")
}
}
const scaffold = Scaffold(config)
})
```
### Additional Node.js options
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:
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.
```javascript
config.output = (fullPath, baseDir, baseName) => {
console.log({ fullPath, baseDir, baseName })
return path.resolve(baseDir, baseName)
}
```
## Preparing files
@@ -169,20 +146,8 @@ 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(),
}
```
**Note:** These helpers are available for any data property, not exclusive to `name`.
## Examples
@@ -209,15 +174,15 @@ simple-scaffold MyComponent \
- ...
```
#### Contents of `project/scaffold/{{Name}}.jsx`
#### Contents of `project/scaffold/{{Name}}.js`
```js
const React = require('react')
module.exports = function {{Name}}(props) {
return (
module.exports = class {{Name}} extends React.Component {
render() {
<div className="{{className}}">{{Name}} Component</div>
)
}
}
```
@@ -244,42 +209,14 @@ With `createSubFolder = false`:
- ...
```
#### Contents of `project/scaffold/MyComponent/MyComponent.jsx`
#### Contents of `project/scaffold/MyComponent/MyComponent.js`
```js
const React = require("react")
module.exports = function MyComponent(props) {
return (
module.exports = class MyComponent extends React.Component {
render() {
<div className="myClassName">MyComponent Component</div>
)
}
}
```
## Contributing
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.
If you are a developer and want to contribute code, here are some starting tips:
1. Fork this repository
2. Run `yarn install`
3. Run `yarn dev` to start file watch mode
4. Make any changes you would like
5. Create tests for your changes
6. Update the relevant documentation (readme, code comments, type comments)
7. Create a PR on upstream
Some tips on getting around the code:
- Use `yarn dev` for development - it runs TypeScript compile in watch mode, allowing you to make
changes and immediately be able to try them using `yarn cmd`.
- Use `yarn build` to build the output
- Use `yarn test` to run tests
- Use `yarn cmd` to use the CLI feature of Simple Scaffold from within the root directory,
enabling you to test different behaviors. See `yarn cmd -h` for more information.
> This requires an updated build, and does not trigger one itself. Either use `yarn dev` or
> `yarn build` before running this, or use `yarn build-cmd` instead, which triggers a build right
> before running the command with the rest of the given arguments.

View File

@@ -1,6 +1,6 @@
{
"name": "simple-scaffold",
"version": "1.0.0",
"version": "1.0.0-alpha.11",
"description": "Create files based on templates",
"repository": "https://github.com/chenasraf/simple-scaffold.git",
"author": "Chen Asraf <inbox@casraf.com>",
@@ -9,7 +9,7 @@
"bin": "cmd.js",
"scripts": {
"clean": "rm -rf dist/",
"build": "yarn clean && tsc && chmod -R +x ./dist && cp ./package.json ./README.md ./dist/",
"build": "yarn clean && tsc && chmod -R +x ./dist && cp ./package.json ./dist/ && cp ./README.md ./dist/",
"dev": "tsc --watch",
"start": "node dist/scaffold.js",
"test": "jest --verbose",
@@ -23,7 +23,7 @@
"glob": "^7.1.3",
"handlebars": "^4.7.7",
"lodash": "^4.17.21",
"massarg": "^1.0.5",
"massarg": "^1.0.3",
"util.promisify": "^1.1.1"
},
"devDependencies": {

View File

@@ -1,97 +1,2 @@
#!/usr/bin/env node
import massarg from "massarg"
import chalk from "chalk"
import { LogLevel, ScaffoldCmdConfig } from "./types"
import { Scaffold } from "./scaffold"
export function parseCliArgs(args = process.argv.slice(2)) {
return (
massarg<ScaffoldCmdConfig & { help: boolean; extras: string[] }>()
.main(Scaffold)
.option({
name: "name",
aliases: ["n"],
description:
"Name to be passed to the generated files. {{name}} and {{Name}} inside contents and file names will be replaced accordingly.",
isDefault: true,
required: true,
})
.option({
name: "output",
aliases: ["o"],
description: `Path to output to. If --create-sub-folder is enabled, the subfolder will be created inside this path. ${chalk.reset`${chalk.white`(default: current dir)`}`}`,
required: true,
})
.option({
name: "templates",
aliases: ["t"],
array: true,
description:
"Template files to use as input. You may provide multiple files, each of which can be a relative or absolute path, " +
"or a glob pattern for multiple file matching easily.",
required: true,
})
.option({
name: "overwrite",
aliases: ["w"],
boolean: true,
defaultValue: false,
description: "Enable to override output files, even if they already exist.",
})
.option({
name: "data",
aliases: ["d"],
description: "Add custom data to the templates. By default, only your app name is included.",
parse: (v) => JSON.parse(v),
})
.option({
name: "create-sub-folder",
aliases: ["s"],
boolean: true,
defaultValue: false,
description: "Create subfolder with the input name",
})
.option({
name: "quiet",
aliases: ["q"],
boolean: true,
defaultValue: false,
description: "Suppress output logs (Same as --verbose 0)",
})
.option({
name: "verbose",
aliases: ["v"],
defaultValue: LogLevel.Info,
description: `Determine amount of logs to display. The values are: ${chalk.bold`0 (none) | 1 (debug) | 2 (info) | 3 (warn) | 4 (error)`}. The provided level will display messages of the same level or higher.`,
parse: Number,
})
.option({
name: "dry-run",
aliases: ["dr"],
boolean: true,
defaultValue: false,
description:
"Don't emit files. This is good for testing your scaffolds and making sure they " +
"don't fail, without having to write actual file contents or create directories.",
})
// .example({
// input: `yarn cmd -t examples/test-input/Component -o examples/test-output -d '{"property":"myProp","value":"10"}'`,
// description: "Usage",
// output: "",
// })
.help({
binName: "simple-scaffold",
useGlobalColumns: true,
usageExample: "[options]",
header: "Create structured files based on templates.",
footer: [
`Copyright © Chen Asraf 2021`,
`NPM: ${chalk.underline`https://npmjs.com/package/simple-scaffold`}`,
`GitHub: ${chalk.underline`https://github.com/chenasraf/simple-scaffold`}`,
].join("\n"),
})
.parse(args)
)
}
import { parseCliArgs } from "./cmd_util"
parseCliArgs()

89
src/cmd_util.ts Normal file
View File

@@ -0,0 +1,89 @@
import massarg from "massarg"
import chalk from "chalk"
import { LogLevel, ScaffoldCmdConfig } from "./types"
import { Scaffold } from "./scaffold"
export function parseCliArgs(args = process.argv.slice(2)) {
return (
massarg<ScaffoldCmdConfig & { help: boolean; extras: string[] }>()
.main(Scaffold)
.option({
name: "name",
aliases: ["n"],
isDefault: true,
description:
"Name to be passed to the generated files. {{name}} and {{Name}} inside contents and file names will be replaced accordingly.",
})
.option({
name: "output",
aliases: ["o"],
description:
"Path to output to. If --create-sub-folder is enabled, the subfolder will be created inside this path.",
})
.option({
name: "templates",
aliases: ["t"],
description:
"Template files to use as input. You may provide multiple files, each of which can be a relative or absolute path, " +
"or a glob pattern for multiple file matching easily.",
defaultValue: [],
array: true,
})
.option({
aliases: ["w"],
name: "overwrite",
description: "Enable to override output files, even if they already exist.",
defaultValue: false,
boolean: true,
})
.option({
aliases: ["d"],
name: "data",
description: "Add custom data to the templates. By default, only your app name is included.",
parse: (v) => JSON.parse(v),
})
.option({
aliases: ["s"],
name: "create-sub-folder",
description: "Create subfolder with the input name",
defaultValue: false,
boolean: true,
})
.option({
aliases: ["q"],
name: "quiet",
description: "Suppress output logs (Same as --verbose 0)",
defaultValue: false,
boolean: true,
})
.option({
aliases: ["v"],
name: "verbose",
description: `Determine amount of logs to display. The values are: ${chalk.bold`0 (none) | 1 (debug) | 2 (info) | 3 (warn) | 4 (error)`}. The provided level will display messages of the same level or higher.`,
defaultValue: LogLevel.Info,
parse: Number,
})
.option({
aliases: ["dr"],
name: "dry-run",
description:
"Don't emit files. This is good for testing your scaffolds and making sure they " +
"don't fail, without having to write actual file contents or create directories.",
defaultValue: false,
boolean: true,
})
// .example({
// input: `yarn cmd -t examples/test-input/Component -o examples/test-output -d '{"property":"myProp","value":"10"}'`,
// description: "Usage",
// output: "",
// })
.help({
binName: "simple-scaffold",
useGlobalColumns: true,
usageExample: "[options]",
header: "Create structured files based on templates.",
footer: `Copyright © Chen Asraf 2021\nNPM: ${chalk.underline`https://npmjs.com/package/massarg`}\nGitHub: ${chalk.underline`https://github.com/chenasraf/massarg`}`,
})
.parse(args)
)
}

View File

@@ -15,16 +15,13 @@ import {
isDir,
removeGlob,
makeRelativePath,
registerHelpers,
} from "./utils"
import { LogLevel, ScaffoldConfig } from "./types"
export async function Scaffold({ ...options }: ScaffoldConfig) {
options.output ??= process.cwd()
registerHelpers(options)
export async function Scaffold(config: ScaffoldConfig) {
const options = { ...config }
try {
options.data = { name: options.name, Name: pascalCase(options.name), ...options.data }
const data = { name: options.name, Name: pascalCase(options.name), ...options.data }
log(options, LogLevel.Debug, "Full config:", {
name: options.name,
templates: options.templates,
@@ -33,15 +30,14 @@ export async function Scaffold({ ...options }: 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!
)})`,
})
log(options, LogLevel.Info, "Data:", options.data)
for (let template of options.templates) {
log(options, LogLevel.Info, "Data:", data)
for (let template of config.templates) {
try {
const _isGlob = glob.hasMagic(template)
const _isGlob = template.includes("*")
if (!_isGlob && !(await pathExists(template))) {
const err: NodeJS.ErrnoException = new Error(`ENOENT, no such file or directory ${template}`)
err.code = "ENOENT"
@@ -61,34 +57,37 @@ export async function Scaffold({ ...options }: ScaffoldConfig) {
log(options, LogLevel.Debug, "before glob")
const files = await promisify(glob)(template, {
dot: true,
debug: options.verbose === LogLevel.Debug,
nodir: true,
debug: false,
nodir: options.verbose === LogLevel.Debug,
nobrace: true,
noext: true,
nocomment: true,
nonegate: true,
})
log(options, LogLevel.Debug, "after glob")
for (const inputFilePath of files) {
if (await isDir(inputFilePath)) {
continue
if (!(await isDir(inputFilePath))) {
const relPath = makeRelativePath(path.dirname(removeGlob(inputFilePath).replace(_nonGlobTemplate, "")))
const basePath = path
.resolve(process.cwd(), relPath)
.replace(process.cwd() + "/", "")
.replace(process.cwd(), "")
log(
options,
LogLevel.Debug,
`\nprocess.cwd(): ${process.cwd()}`,
`\norigTemplate: ${origTemplate}`,
`\nrelPath: ${relPath}`,
`\ntemplate: ${template}`,
`\ninputFilePath: ${inputFilePath}`,
`\nnonGlobTemplate: ${_nonGlobTemplate}`,
`\nbasePath: ${basePath}`,
`\nisDir: ${_isDir}`,
`\nisGlob: ${_isGlob}`,
`\n`
)
await handleTemplateFile(inputFilePath, basePath, options, data)
}
const relPath = makeRelativePath(path.dirname(removeGlob(inputFilePath).replace(_nonGlobTemplate, "")))
const basePath = path
.resolve(process.cwd(), relPath)
.replace(process.cwd() + "/", "")
.replace(process.cwd(), "")
log(
options,
LogLevel.Debug,
`\nprocess.cwd(): ${process.cwd()}`,
`\norigTemplate: ${origTemplate}`,
`\nrelPath: ${relPath}`,
`\ntemplate: ${template}`,
`\ninputFilePath: ${inputFilePath}`,
`\nnonGlobTemplate: ${_nonGlobTemplate}`,
`\nbasePath: ${basePath}`,
`\nisDir: ${_isDir}`,
`\nisGlob: ${_isGlob}`,
`\n`
)
await handleTemplateFile(inputFilePath, basePath, options, options.data)
}
} catch (e: any) {
handleErr(e)
@@ -109,12 +108,12 @@ async function handleTemplateFile(
return new Promise(async (resolve, reject) => {
try {
const inputPath = path.resolve(process.cwd(), templatePath)
const outputPathOpt = getOptionValueForFile(options, inputPath, data, options.output)
const outputPathOpt = getOptionValueForFile(inputPath, data, options.output)
const outputDir = path.resolve(
process.cwd(),
...([outputPathOpt, basePath, options.createSubFolder ? options.name : undefined].filter(Boolean) as string[])
)
const outputPath = handlebarsParse(options, path.join(outputDir, path.basename(inputPath)), data).toString()
const outputPath = handlebarsParse(path.join(outputDir, path.basename(inputPath)), data)
log(
options,
LogLevel.Debug,
@@ -126,7 +125,7 @@ async function handleTemplateFile(
`\nFull output path: ${outputPath}`,
`\n`
)
const overwrite = getOptionValueForFile(options, inputPath, data, options.overwrite ?? false)
const overwrite = getOptionValueForFile(inputPath, data, options.overwrite ?? false)
const exists = await pathExists(outputPath)
await createDirIfNotExists(path.dirname(outputPath), options)
@@ -137,7 +136,7 @@ async function handleTemplateFile(
log(options, LogLevel.Info, `File ${outputPath} exists, overwriting`)
}
const templateBuffer = await readFile(inputPath)
const outputContents = handlebarsParse(options, templateBuffer, data)
const outputContents = handlebarsParse(templateBuffer, data)
if (!options.dryRun) {
await writeFile(outputPath, outputContents)

View File

@@ -11,76 +11,18 @@ export type FileResponseFn<T> = (fullPath: string, basedir: string, basename: st
export type FileResponse<T> = T | FileResponseFn<T>
export interface ScaffoldConfig {
/**
* Name to be passed to the generated files. `{{name}}` and `{{Name}}` inside contents and file names will be replaced
* accordingly.
*/
/** The name supplied for the output templates */
name: string
/**
* Template files to use as input. You may provide multiple files, each of which can be a relative or absolute path,
* or a glob pattern for multiple file matching easily. (default: current working directory)
*/
/** Template input files/dirs/glob patterns to use as template input. These will be copied to the output directory. */
templates: string[]
/** Path to output to. If `createSubFolder` is `true`, the subfolder will be created inside this path. */
/** Output directory to put scaffolded files in. */
output: FileResponse<string>
/**
* Create subfolder with the input name (default: `false`)
*/
createSubFolder?: boolean
/**
* Add custom data to the templates. By default, only your app name is included as `{{name}}` and `{{Name}}`.
*/
data?: Record<string, string>
/**
* Enable to override output files, even if they already exist. (default: `false`)
*
* You may supply a function to this option, which can take the arguments `(fullPath, baseDir, baseName)` and returns
* a string, to return a dynamic path for each file.
*/
overwrite?: FileResponse<boolean>
/** Suppress output logs (Same as `verbose: 0` or `verbose: LogLevel.None`) */
quiet?: boolean
/**
* Determine amount of logs to display.
*
* The values are: `0 (none) | 1 (debug) | 2 (info) | 3 (warn) | 4 (error)`. The provided level will display messages
* of the same level or higher. (default: `2 (info)`)
* @see LogLevel
*/
verbose?: LogLevel
/**
* Don't emit files. This is good for testing your scaffolds and making sure they don't fail, without having to write
* 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
@@ -90,6 +32,5 @@ export interface ScaffoldCmdConfig {
data?: Record<string, string>
overwrite: boolean
quiet: boolean
verbose: LogLevel
dryRun: boolean
}

View File

@@ -10,23 +10,17 @@ import { promises as fsPromises } from "fs"
import chalk from "chalk"
const { stat, access, mkdir } = fsPromises
export const defaultHelpers: Exclude<ScaffoldConfig["helpers"], undefined> = {
const helpers = {
camelCase,
snakeCase,
startCase,
kebabCase,
hyphenCase: kebabCase,
pascalCase,
lowerCase: (text) => text.toLowerCase(),
upperCase: (text) => text.toUpperCase(),
}
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])
}
for (const helperName in helpers) {
Handlebars.registerHelper(helperName, helpers[helperName as keyof typeof helpers])
}
export function handleErr(err: NodeJS.ErrnoException | null) {
@@ -80,7 +74,6 @@ export async function createDirIfNotExists(dir: string, options: ScaffoldConfig)
}
export function getOptionValueForFile<T>(
options: ScaffoldConfig,
filePath: string,
data: Record<string, string>,
fn: FileResponse<T>,
@@ -91,24 +84,15 @@ export function getOptionValueForFile<T>(
}
return (fn as FileResponseFn<T>)(
filePath,
path.dirname(handlebarsParse(options, filePath, data).toString()),
path.basename(handlebarsParse(options, filePath, data).toString())
path.dirname(handlebarsParse(filePath, data)),
path.basename(handlebarsParse(filePath, data))
)
}
export function handlebarsParse(
options: ScaffoldConfig,
templateBuffer: Buffer | string,
data: Record<string, string>
) {
try {
const parser = Handlebars.compile(templateBuffer.toString(), { noEscape: true })
const outputContents = parser(data)
return outputContents
} catch {
log(options, LogLevel.Warning, "Couldn't parse file with handlebars, returning original content")
return templateBuffer
}
export function handlebarsParse(templateBuffer: Buffer | string, data: Record<string, string>) {
const parser = Handlebars.compile(templateBuffer.toString(), { noEscape: true })
const outputContents = parser(data)
return outputContents
}
export async function pathExists(filePath: string): Promise<boolean> {

View File

@@ -3,7 +3,6 @@ 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: {
@@ -31,20 +30,6 @@ 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 {
@@ -217,59 +202,4 @@ 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

@@ -716,9 +716,9 @@ ansi-escapes@^4.2.1:
type-fest "^0.21.3"
ansi-regex@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
ansi-styles@^3.2.1:
version "3.2.1"
@@ -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.0.0, chalk@^4.1.1:
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.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/massarg/-/massarg-1.0.5.tgz#3dfd49bb63bfca4b0371a2f0ffc7580a9a6cc7a0"
integrity sha512-gkJHZsNfeMurpVPKojCiT2lnG2cSxHPAXZSg+gCLIgCf5bGn7pKOsoeu4qVAc5mlBxV2l/hA1yojt+ZsJd8SDg==
massarg@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/massarg/-/massarg-1.0.3.tgz#228cf2d84896924b6b12021ea23ed9a9077e15b7"
integrity sha512-LYb4XvAQ+PbBClyfkn9B4JtfwycfpnOnGIznALt9YLnrmQaCcXXJBQsG5SA/2w+bmLOeYRoR9GqqFLyaniCn9g==
dependencies:
chalk "^4.1.2"
chalk "^4.1.1"
lodash "^4.17.21"
merge-stream@^2.0.0: