Compare commits

..

45 Commits

Author SHA1 Message Date
Chen Asraf
8782f18a73 v1.0.1
Merge pull request #24 from chenasraf/alpha
2022-02-20 14:10:59 +02:00
Chen Asraf
21c4ab6e1a bump version number [skip ci] 2022-02-20 14:06:56 +02:00
Chen Asraf
d797e5b640 bump alpha version number 2022-02-17 19:19:07 +02:00
Chen Asraf
c3835a7b04 refactoring - code cleanup 2022-02-17 19:18:48 +02:00
Chen Asraf
81ba5f50fd add subFolderNameHelper arg 2022-02-17 19:12:17 +02:00
Chen Asraf
edcf1aceaa Update README.md 2022-02-14 23:34:59 +02:00
Chen Asraf
d0a0db0f82 Update MIGRATION.md [skip ci] 2022-02-14 23:31:52 +02:00
Chen Asraf
819f84e5ca Merge pull request #23 from chenasraf/dependabot/npm_and_yarn/ansi-regex-5.0.1
Bump ansi-regex from 5.0.0 to 5.0.1
2022-02-14 23:29:41 +02:00
dependabot[bot]
36f8b87514 Bump ansi-regex from 5.0.0 to 5.0.1
Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/chalk/ansi-regex/releases)
- [Commits](https://github.com/chalk/ansi-regex/compare/v5.0.0...v5.0.1)

---
updated-dependencies:
- dependency-name: ansi-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-14 21:28:17 +00:00
Chen Asraf
d06c0d64b3 bump version: v1.0.0 2022-02-14 23:26:48 +02:00
Chen Asraf
5ab2637485 bump version number 2022-02-14 23:26:48 +02:00
Chen Asraf
0af639254b fail handlebars parse silently 2022-02-14 23:26:48 +02:00
Chen Asraf
391a08ac63 update README.md, default output fix 2022-02-14 23:26:48 +02:00
Chen Asraf
cd34930e04 fix cmd 2022-02-14 23:26:48 +02:00
Chen Asraf
956b00700f update workflows [skip publish] 2022-02-14 23:26:48 +02:00
Chen Asraf
91116bba69 update alpha workflow 2022-02-14 23:26:48 +02:00
Chen Asraf
f4cc44cf17 helpers fix 2022-02-14 23:26:48 +02:00
Chen Asraf
54b90235e5 fix copyright 2022-02-14 23:26:48 +02:00
Chen Asraf
d03d0e0812 added custom helpers 2022-02-14 23:26:48 +02:00
Chen Asraf
5b72b6c166 update deps + add MIGRATION.md 2022-02-14 23:26:48 +02:00
Chen Asraf
d96992cd2d update deps + update cmd requirements 2022-02-14 23:26:48 +02:00
Chen Asraf
925993948a update readme [skip ci] 2022-02-14 23:26:48 +02:00
Chen Asraf
09403e18f1 update readme [skip ci] 2022-02-14 23:26:48 +02:00
Chen Asraf
c2bc8b7606 update docs [skip publish] 2022-02-14 23:26:48 +02:00
Chen Asraf
b1b1aca802 fix basename in some cases 2022-02-14 23:26:48 +02:00
Chen Asraf
2305083c7d run tests on ci [skip publish] 2022-02-14 23:26:48 +02:00
Chen Asraf
8413225202 fix errors, fix nested output 2022-02-14 23:26:48 +02:00
Chen Asraf
559b5ad7f3 remove types from package.json 2022-02-14 23:26:48 +02:00
Chen Asraf
a21a35f773 fix yarn.lock 2022-02-14 23:26:48 +02:00
Chen Asraf
84e6207891 fix log level 0 2022-02-14 23:26:48 +02:00
Chen Asraf
6b57406369 add export for cmd_util 2022-02-14 23:26:48 +02:00
Chen Asraf
53e8bc4cc0 bump version number 2022-02-14 23:26:48 +02:00
Chen Asraf
54848f9c50 update workflows [skip publish] 2022-02-14 23:26:48 +02:00
Chen Asraf
2623b787e6 fixes + add log level [skip publish] 2022-02-14 23:26:48 +02:00
Chen Asraf
ad30ee0c0c fix readme [skip publish] 2022-02-14 23:26:48 +02:00
Chen Asraf
8575b1e0c3 try fix release upload 2022-02-14 23:26:48 +02:00
Chen Asraf
d8aba21d0e try new release version 2022-02-14 23:26:48 +02:00
Chen Asraf
3f2945eaa4 fixed release tarball file location 2022-02-14 23:26:48 +02:00
Chen Asraf
930344656a fixed release tarball file location 2022-02-14 23:26:48 +02:00
Chen Asraf
99c9055208 fix build output files 2022-02-14 23:26:48 +02:00
Chen Asraf
f1698d2a46 add build step 2022-02-14 23:26:48 +02:00
Chen Asraf
c17e6304e6 try to fix workflow 2022-02-14 23:26:48 +02:00
Chen Asraf
535260a0c0 publish: debug mode off, try to fix workflow 2022-02-14 23:26:48 +02:00
Chen Asraf
14988576f3 publish: debug mode on 2022-02-14 23:26:48 +02:00
Chen Asraf
6f03ed9d60 try fix workflow 2022-02-14 23:26:48 +02:00
10 changed files with 390 additions and 129 deletions

View File

@@ -3,10 +3,11 @@
"npm.packageManager": "yarn",
"cSpell.words": [
"massarg",
"nodir",
"nobrace",
"noext",
"nocomment",
"nonegate"
"nodir",
"noext",
"nonegate",
"subdir"
]
}

View File

@@ -5,6 +5,7 @@ between versions. With these notable exceptions:
- Some of the argument names have changed
- Template syntax has been improved
- The command to run Scaffold has been simplified from `new SimpleScaffold(opts).run()` to `SimpleScaffold(opts)`, which now returns a promise that you can await to know when the process has been completed.
## Argument changes

View File

@@ -36,40 +36,45 @@ Create structured files based on templates.
Options:
--help|-h Display help information
--help|-h Display help information
--name|-n Name to be passed to the generated files. {{name}} and
{{Name}} inside contents and file names will be replaced
accordingly.
--name|-n Name to be passed to the generated files. {{name}} and
{{Name}} inside contents and file names will be replaced
accordingly.
--output|-o Path to output to. If --create-sub-folder is enabled, the
subfolder will be created inside this path.
--output|-o Path to output to. If --create-sub-folder is enabled,
the subfolder will be created inside this path.
(default: current dir)
--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)
--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.
--overwrite|-w Enable to override output files, even if they already exist.
(default: false)
--overwrite|-w Enable to override output files, even if they already
exist. (default: false)
--data|-d Add custom data to the templates. By default, only your app
name is included.
--data|-d Add custom data to the templates. By default, only your
app name is included.
--create-sub-folder|-s Create subfolder with the input name (default:
false)
--create-sub-folder|-s Create subfolder with the input name
(default: false)
--quiet|-q Suppress output logs (Same as --verbose 0)
(default: false)
--sub-folder-name-helper|-sh Default helper to apply to subfolder name when using
`--create-sub-folder true`.
--verbose|-v 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)
--quiet|-q Suppress output logs (Same as --verbose 0)
(default: false)
--dry-run|-dr 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)
--verbose|-v 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)
--dry-run|-dr 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)
```
You can also add this as a script in your `package.json`:
@@ -98,6 +103,7 @@ const config = {
templates: [path.join(__dirname, "scaffolds", "component")],
output: path.join(__dirname, "src", "components"),
createSubFolder: true,
subFolderNameHelper: "upperCase"
locals: {
property: "value",
},
@@ -184,6 +190,9 @@ config.helpers = {
}
```
These helpers will also be available to you when using `subFolderNameHelper` or
`--sub-folder-name-helper` as a possible value.
## Examples
### Command Example
@@ -280,6 +289,6 @@ Some tips on getting around the code:
- 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.
> This requires an updated build, and does not trigger one itself. Either use `yarn dev` to watch
> for changes and build, 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-alpha.18",
"version": "1.0.1",
"description": "Create files based on templates",
"repository": "https://github.com/chenasraf/simple-scaffold.git",
"author": "Chen Asraf <inbox@casraf.com>",

View File

@@ -51,6 +51,11 @@ export function parseCliArgs(args = process.argv.slice(2)) {
defaultValue: false,
description: "Create subfolder with the input name",
})
.option({
name: "sub-folder-name-helper",
aliases: ["sh"],
description: "Default helper to apply to subfolder name when using `--create-sub-folder true`.",
})
.option({
name: "quiet",
aliases: ["q"],

View File

@@ -16,79 +16,81 @@ import {
removeGlob,
makeRelativePath,
registerHelpers,
getTemplateGlobInfo,
ensureFileExists,
getFileList,
getBasePath,
copyFileTransformed,
getTemplateFileInfo,
logInitStep,
logInputFile,
GlobInfo,
OutputFileInfo,
} from "./utils"
import { LogLevel, ScaffoldConfig } from "./types"
import { FileResponse, LogLevel, ScaffoldConfig } from "./types"
/**
* Create a scaffold using given `options`.
*
* #### Create files
* To create a file structure to output, use any directory and file structure you would like.
* Inside folder names, file names or file contents, you may place `{{ var }}` where `var` is either
* `name` which is the scaffold name you provided or one of the keys you provided in the `data` option.
*
* The contents and names will be replaced with the transformed values so you can use your original structure as a
* boilerplate for other projects, components, modules, or even single files.
*
* #### Helpers
* Helpers are functions you can use to transform your `{{ var }}` contents into other values without having to
* pre-define the data and use a duplicated key. Common cases are transforming name-case format
* (e.g. `MyName` &rarr; `my_name`), so these have been provided as defaults:
*
* | 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 |
*
* 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) {
options.output ??= process.cwd()
registerHelpers(options)
try {
options.data = { name: options.name, Name: pascalCase(options.name), ...options.data }
log(options, LogLevel.Debug, "Full config:", {
name: options.name,
templates: options.templates,
output: options.output,
createSubfolder: options.createSubFolder,
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) {
logInitStep(options)
for (let _template of options.templates) {
try {
const _isGlob = glob.hasMagic(template)
if (!_isGlob && !(await pathExists(template))) {
const err: NodeJS.ErrnoException = new Error(`ENOENT, no such file or directory ${template}`)
err.code = "ENOENT"
err.path = "non-existing-input"
err.errno = -2
throw err
}
const _nonGlobTemplate = _isGlob ? removeGlob(template) : template
log(options, LogLevel.Debug, "before isDir", "isGlob:", _isGlob, template)
const _isDir = _isGlob ? true : await isDir(template)
log(options, LogLevel.Debug, "after isDir", _isDir)
const _shouldAddGlob = !_isGlob && _isDir
const origTemplate = template
if (_shouldAddGlob) {
template = template + "/**/*"
}
log(options, LogLevel.Debug, "before glob")
const files = await promisify(glob)(template, {
dot: true,
debug: options.verbose === LogLevel.Debug,
nodir: true,
})
log(options, LogLevel.Debug, "after glob")
const { nonGlobTemplate, origTemplate, isDirOrGlob, isGlob, template } = await getTemplateGlobInfo(
options,
_template
)
await ensureFileExists(template, isDirOrGlob)
const files = await getFileList(options, template)
for (const inputFilePath of files) {
if (await isDir(inputFilePath)) {
continue
}
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)
const relPath = makeRelativePath(path.dirname(removeGlob(inputFilePath).replace(nonGlobTemplate, "")))
const basePath = getBasePath(relPath)
logInputFile(options, {
origTemplate,
relPath,
template,
inputFilePath,
nonGlobTemplate,
basePath,
isDirOrGlob,
isGlob,
})
await handleTemplateFile(options, options.data, { templatePath: inputFilePath, basePath })
}
} catch (e: any) {
handleErr(e)
@@ -99,22 +101,19 @@ export async function Scaffold({ ...options }: ScaffoldConfig) {
throw e
}
}
async function handleTemplateFile(
templatePath: string,
basePath: string,
options: ScaffoldConfig,
data: Record<string, string>
data: Record<string, string>,
{ templatePath, basePath }: { templatePath: string; basePath: string }
): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
const inputPath = path.resolve(process.cwd(), templatePath)
const outputPathOpt = getOptionValueForFile(options, 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 { inputPath, outputPathOpt, outputDir, outputPath, exists } = await getTemplateFileInfo(options, data, {
templatePath,
basePath,
})
const overwrite = getOptionValueForFile(options, inputPath, data, options.overwrite ?? false)
log(
options,
LogLevel.Debug,
@@ -126,29 +125,11 @@ async function handleTemplateFile(
`\nFull output path: ${outputPath}`,
`\n`
)
const overwrite = getOptionValueForFile(options, inputPath, data, options.overwrite ?? false)
const exists = await pathExists(outputPath)
await createDirIfNotExists(path.dirname(outputPath), options)
log(options, LogLevel.Info, `Writing to ${outputPath}`)
if (!exists || overwrite) {
if (exists && overwrite) {
log(options, LogLevel.Info, `File ${outputPath} exists, overwriting`)
}
const templateBuffer = await readFile(inputPath)
const outputContents = handlebarsParse(options, templateBuffer, data)
if (!options.dryRun) {
await writeFile(outputPath, outputContents)
log(options, LogLevel.Info, "Done.")
} else {
log(options, LogLevel.Info, "Content output:")
log(options, LogLevel.Info, outputContents)
}
} else if (exists) {
log(options, LogLevel.Info, `File ${outputPath} already exists, skipping`)
}
await copyFileTransformed(options, data, { exists, overwrite, outputPath, inputPath })
resolve()
} catch (e: any) {
handleErr(e)

View File

@@ -10,6 +10,20 @@ export type FileResponseFn<T> = (fullPath: string, basedir: string, basename: st
export type FileResponse<T> = T | FileResponseFn<T>
export type DefaultHelperKeys =
| "camelCase"
| "snakeCase"
| "startCase"
| "kebabCase"
| "hyphenCase"
| "pascalCase"
| "lowerCase"
| "upperCase"
export type HelperKeys<T> = DefaultHelperKeys | T
export type Helper = (text: string) => string
export interface ScaffoldConfig {
/**
* Name to be passed to the generated files. `{{name}}` and `{{Name}}` inside contents and file names will be replaced
@@ -79,8 +93,26 @@ export interface ScaffoldConfig {
* }
* })
* ```
*
* 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, (text: string) => string>
helpers?: Record<string, Helper>
/**
* Default transformer to apply to subfolder name when using `createSubFolder: true`. Can be one of the default
* helpers, or a custom one you provide to `helpers`. Defaults to `undefined`, which means no transformation is done.
*/
subFolderNameHelper?: DefaultHelperKeys | string
}
export interface ScaffoldCmdConfig {
name: string

View File

@@ -1,6 +1,6 @@
import path from "path"
import { F_OK } from "constants"
import { FileResponse, FileResponseFn, LogLevel, ScaffoldConfig } from "./types"
import { DefaultHelperKeys, FileResponse, FileResponseFn, Helper, LogLevel, ScaffoldConfig } from "./types"
import camelCase from "lodash/camelCase"
import snakeCase from "lodash/snakeCase"
import kebabCase from "lodash/kebabCase"
@@ -10,7 +10,11 @@ import { promises as fsPromises } from "fs"
import chalk from "chalk"
const { stat, access, mkdir } = fsPromises
export const defaultHelpers: Exclude<ScaffoldConfig["helpers"], undefined> = {
import { glob } from "glob"
import { promisify } from "util"
const { readFile, writeFile } = fsPromises
export const defaultHelpers: Record<DefaultHelperKeys, Helper> = {
camelCase,
snakeCase,
startCase,
@@ -34,7 +38,7 @@ export function handleErr(err: NodeJS.ErrnoException | null) {
}
export function log(options: ScaffoldConfig, level: LogLevel, ...obj: any[]) {
if (options.quiet || options.verbose === LogLevel.None || level <= (options.verbose ?? LogLevel.Info)) {
if (options.quiet || options.verbose === LogLevel.None || level < (options.verbose ?? LogLevel.Info)) {
return
}
const levelColor: Record<LogLevel, keyof typeof chalk> = {
@@ -139,3 +143,177 @@ export function removeGlob(template: string) {
export function makeRelativePath(str: string): string {
return str.startsWith("/") ? str.slice(1) : str
}
export function getBasePath(relPath: string) {
return path
.resolve(process.cwd(), relPath)
.replace(process.cwd() + "/", "")
.replace(process.cwd(), "")
}
export async function getFileList(options: ScaffoldConfig, template: string) {
return await promisify(glob)(template, {
dot: true,
debug: options.verbose === LogLevel.Debug,
nodir: true,
})
}
export interface GlobInfo {
nonGlobTemplate: string
origTemplate: string
isDirOrGlob: boolean
isGlob: boolean
template: string
}
export async function getTemplateGlobInfo(options: ScaffoldConfig, template: string): Promise<GlobInfo> {
const isGlob = glob.hasMagic(template)
log(options, LogLevel.Debug, "before isDir", "isGlob:", isGlob, template)
let _template = template
const nonGlobTemplate = isGlob ? removeGlob(template) : template
const isDirOrGlob = isGlob ? true : await isDir(template)
log(options, LogLevel.Debug, "after isDir", isDirOrGlob)
const _shouldAddGlob = !isGlob && isDirOrGlob
const origTemplate = template
if (_shouldAddGlob) {
_template = template + "/**/*"
}
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
outputDir: string
outputPath: string
exists: boolean
}
export async function getTemplateFileInfo(
options: ScaffoldConfig,
data: Record<string, string>,
{ 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 outputPath = handlebarsParse(options, path.join(outputDir, path.basename(inputPath)), data).toString()
const exists = await pathExists(outputPath)
return { inputPath, outputPathOpt, outputDir, outputPath, exists }
}
export async function copyFileTransformed(
options: ScaffoldConfig,
data: Record<string, string>,
{
exists,
overwrite,
outputPath,
inputPath,
}: { exists: boolean; overwrite: boolean; outputPath: string; inputPath: string }
) {
if (!exists || overwrite) {
if (exists && overwrite) {
log(options, LogLevel.Info, `File ${outputPath} exists, overwriting`)
}
const templateBuffer = await readFile(inputPath)
const outputContents = handlebarsParse(options, templateBuffer, data)
if (!options.dryRun) {
await writeFile(outputPath, outputContents)
log(options, LogLevel.Info, "Done.")
} else {
log(options, LogLevel.Info, "Content output:")
log(options, LogLevel.Info, outputContents)
}
} 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
) {
return path.resolve(
process.cwd(),
...([
outputPathOpt,
basePath,
options.createSubFolder
? options.subFolderNameHelper
? handlebarsParse(options, `{{ ${options.subFolderNameHelper} name }}`, data)
: options.name
: undefined,
].filter(Boolean) as string[])
)
}
export function logInputFile(
options: ScaffoldConfig,
{
origTemplate,
relPath,
template,
inputFilePath,
nonGlobTemplate,
basePath,
isDirOrGlob,
isGlob,
}: {
origTemplate: string
relPath: string
template: string
inputFilePath: string
nonGlobTemplate: string
basePath: string
isDirOrGlob: boolean
isGlob: boolean
}
) {
log(
options,
LogLevel.Debug,
`\nprocess.cwd(): ${process.cwd()}`,
`\norigTemplate: ${origTemplate}`,
`\nrelPath: ${relPath}`,
`\ntemplate: ${template}`,
`\ninputFilePath: ${inputFilePath}`,
`\nnonGlobTemplate: ${nonGlobTemplate}`,
`\nbasePath: ${basePath}`,
`\nisDirOrGlob: ${isDirOrGlob}`,
`\nisGlob: ${isGlob}`,
`\n`
)
}
export function logInitStep(options: ScaffoldConfig) {
log(options, LogLevel.Debug, "Full config:", {
name: options.name,
templates: options.templates,
output: options.output,
createSubfolder: options.createSubFolder,
data: options.data,
overwrite: options.overwrite,
quiet: options.quiet,
subFolderTransformHelper: options.subFolderNameHelper,
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)
}

View File

@@ -31,6 +31,12 @@ const fileStructNested = {
},
output: {},
}
const fileStructSubdirTransformer = {
input: {
"{{name}}.txt": "Hello, my app is {{name}}",
},
output: {},
}
const defaultHelperNames = Object.keys(defaultHelpers)
const fileStructHelpers = {
@@ -272,4 +278,52 @@ describe("Scaffold", () => {
})
})
)
describe(
"transform subfolder",
withMock(fileStructSubdirTransformer, () => {
test("should work with no helper", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
createSubFolder: true,
verbose: 0,
})
const data = readFileSync(process.cwd() + "/output/app_name/app_name.txt")
expect(data.toString()).toBe("Hello, my app is app_name")
})
test("should work with default helper", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
createSubFolder: true,
verbose: 0,
subFolderNameHelper: "upperCase",
})
const data = readFileSync(process.cwd() + "/output/APP_NAME/app_name.txt")
expect(data.toString()).toBe("Hello, my app is app_name")
})
test("should work with custom helper", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input"],
createSubFolder: true,
verbose: 0,
subFolderNameHelper: "test",
helpers: {
test: () => "REPLACED",
},
})
const data = readFileSync(process.cwd() + "/output/REPLACED/app_name.txt")
expect(data.toString()).toBe("Hello, my app is app_name")
})
})
)
})

View File

@@ -716,9 +716,9 @@ ansi-escapes@^4.2.1:
type-fest "^0.21.3"
ansi-regex@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^3.2.1:
version "3.2.1"