Skip to content

Commit 2623b78

Browse files
committed
fixes + add log level [skip publish]
1 parent ad30ee0 commit 2623b78

7 files changed

Lines changed: 117 additions & 39 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ typings/
6060
examples/test-output/**/*
6161
dist/
6262
.DS_Store
63+
tmp/

README.md

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,18 @@ Options:
4343
--data|-d Add custom data to the templates. By default, only your app
4444
name is included.
4545
46-
--create-sub-folder|-s Create subfolder with the input name (default:
47-
false)
46+
--create-sub-folder|-s Create subfolder with the input name (default: false)
4847
49-
--quiet|-q Suppress output logs (default:
50-
false)
48+
--quiet|-q Suppress output logs (Same as --verbose 0) (default: false)
5149
52-
--dry-run|-dr Don't emit actual files. This is good for testing your
53-
scaffolds and making sure they don't fail, without having to write
54-
actual files. (default: false)
50+
--verbose|-v Determine amount of logs to display. The values are: 0 (none)
51+
| 1 (debug) | 2 (info) | 3 (warn) | 4 (error). The provided level
52+
will display messages of the same level or higher.
53+
(default: 2)
54+
55+
--dry-run|-dr Don't emit files. This is good for testing your scaffolds and
56+
making sure they don't fail, without having to write actual file
57+
contents or create directories. (default: false)
5558
```
5659

5760
You can also add this as a script in your `package.json`:
@@ -73,15 +76,15 @@ The config takes similar arguments to the command line:
7376
```javascript
7477
const SimpleScaffold = require("simple-scaffold").default
7578

76-
const scaffold = new SimpleScaffold({
79+
const scaffold = SimpleScaffold({
7780
name: "component",
7881
templates: [path.join(__dirname, "scaffolds", "component")],
7982
output: path.join(__dirname, "src", "components"),
8083
createSubFolder: true,
8184
locals: {
8285
property: "value",
8386
},
84-
}).run()
87+
})
8588
```
8689

8790
The exception in the config is that `output`, when used in Node directly, may also be passed a
@@ -120,19 +123,19 @@ Your `data` will be pre-populated with the following:
120123
121124
Simple-Scaffold provides some built-in text transformation filters usable by handleBars.
122125

123-
For example, you may use `{{ name | snakeCase }}` inside a template file or filename, and it will
126+
For example, you may use `{{ snakeCase name }}` inside a template file or filename, and it will
124127
replace `My Name` with `my_name` when producing the final value.
125128

126129
Here are the built-in helpers available for use:
127130

128-
```plaintext
129-
{{ name | camelCase }} => myName
130-
{{ name | snakeCase }} => my_name
131-
{{ name | startCase }} => My Name
132-
{{ name | kebabCase }} => my-name
133-
{{ name | hyphenCase }} => my-name
134-
{{ name | pascalCase }} => MyName
135-
```
131+
| Helper name | Example code | Example output |
132+
| ----------- | ----------------------- | -------------- |
133+
| camelCase | `{{ camelCase name }}` | myName |
134+
| snakeCase | `{{ snakeCase name }}` | my_name |
135+
| startCase | `{{ startCase name }}` | My Name |
136+
| kebabCase | `{{ kebabCase name }}` | my-name |
137+
| hyphenCase | `{{ hyphenCase name }}` | my-name |
138+
| pascalCase | `{{ pascalCase name }}` | MyName |
136139

137140
**Note:** These helpers are available for any data property, not exclusive to `name`.
138141

src/cmd.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Scaffold from "./scaffold"
22
import massarg from "massarg"
33
import { ScaffoldCmdConfig } from "./types"
4+
import { LogLevel } from "."
45

56
massarg<ScaffoldCmdConfig & { help: boolean; extras: string[] }>()
67
.main(Scaffold)
@@ -46,7 +47,21 @@ massarg<ScaffoldCmdConfig & { help: boolean; extras: string[] }>()
4647
defaultValue: false,
4748
boolean: true,
4849
})
49-
.option({ aliases: ["q"], name: "quiet", description: "Suppress output logs", defaultValue: false, boolean: true })
50+
.option({
51+
aliases: ["q"],
52+
name: "quiet",
53+
description: "Suppress output logs (Same as --verbose 0)",
54+
defaultValue: false,
55+
boolean: true,
56+
})
57+
.option({
58+
aliases: ["v"],
59+
name: "verbose",
60+
description:
61+
"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.",
62+
defaultValue: LogLevel.Info,
63+
parse: Number,
64+
})
5065
.option({
5166
aliases: ["dr"],
5267
name: "dry-run",

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./scaffold"
2+
export * from "./types"
23
import Scaffold from "./scaffold"
34
export default Scaffold

src/scaffold.ts

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ import {
1313
pathExists,
1414
pascalCase,
1515
isDir,
16+
removeGlob,
1617
} from "./utils"
17-
import { ScaffoldConfig } from "./types"
18+
import { LogLevel, ScaffoldConfig } from "./types"
1819

1920
export async function Scaffold(config: ScaffoldConfig) {
2021
try {
2122
const options = { ...config }
2223
const data = { name: options.name, Name: pascalCase(options.name), ...options.data }
23-
log(options, "Config:", {
24+
log(options, LogLevel.Debug, "Full config:", {
2425
name: options.name,
2526
templates: options.templates,
2627
output: options.output,
@@ -29,19 +30,33 @@ export async function Scaffold(config: ScaffoldConfig) {
2930
overwrite: options.overwrite,
3031
quiet: options.quiet,
3132
})
32-
log(options, "Data:", data)
33+
log(options, LogLevel.Info, "Data:", data)
3334
for (let template of config.templates) {
3435
try {
35-
const _isDir = await isDir(template)
36-
const basePath = path
37-
.resolve(process.cwd(), _isDir ? template : path.dirname(template.replace("*", "").replace("//", "/")))
38-
.replace(process.cwd(), ".")
39-
if (_isDir) {
36+
const _isGlob = template.includes("*")
37+
const _nonGlobTemplate = _isGlob ? removeGlob(template) : template
38+
const _isDir = _isGlob ? false : await isDir(template)
39+
const _shouldAddGlob = !_isGlob && !_isDir
40+
if (_shouldAddGlob) {
4041
template = template + "/**/*"
4142
}
4243
const files = await promisify(glob)(template, { dot: true, debug: false })
4344
for (const templatePath of files) {
4445
if (!(await isDir(templatePath))) {
46+
const basePath = path
47+
.resolve(
48+
process.cwd(),
49+
_isDir
50+
? templatePath.replace(template, "")
51+
: path.dirname(removeGlob(templatePath).replace(_nonGlobTemplate, ""))
52+
)
53+
.replace(process.cwd() + "/", "")
54+
.replace(process.cwd(), "")
55+
log(
56+
options,
57+
LogLevel.Debug,
58+
`\ntemplate: ${template}\ntemplatePath: ${templatePath}, \nbase path: ${basePath}\n`
59+
)
4560
await handleTemplateFile(templatePath, basePath, options, data)
4661
}
4762
}
@@ -63,35 +78,53 @@ async function handleTemplateFile(
6378
): Promise<void> {
6479
return new Promise(async (resolve, reject) => {
6580
try {
66-
log(options, `Parsing ${templatePath}`)
67-
const inputPath = path.join(process.cwd(), templatePath)
81+
const inputPath = path.resolve(process.cwd(), templatePath)
6882
const outputPathOpt = getOptionValueForFile(inputPath, data, options.output)
6983
const outputDir = path.resolve(
7084
process.cwd(),
71-
...([outputPathOpt, options.createSubFolder ? options.name : undefined].filter(Boolean) as string[])
85+
...([outputPathOpt, basePath, options.createSubFolder ? options.name : undefined].filter(Boolean) as string[])
86+
)
87+
log(
88+
options,
89+
LogLevel.Debug,
90+
`\nParsing ${templatePath}`,
91+
`\nBase path: ${basePath}`,
92+
`\nFull input path: ${inputPath}`,
93+
`\nFull output path: ${outputDir}\n`
7294
)
7395
const outputPath = path.join(outputDir, handlebarsParse(path.basename(inputPath), data))
7496
const overwrite = getOptionValueForFile(inputPath, data, options.overwrite ?? false)
7597
const exists = await pathExists(outputPath)
7698

99+
log(
100+
options,
101+
LogLevel.Debug,
102+
"Filename parsed:",
103+
handlebarsParse(path.basename(inputPath), data),
104+
"Orig:",
105+
path.basename(inputPath)
106+
// "Test:",
107+
// handlebarsParse("{{name}} {{name pascalCase}}", data)
108+
)
109+
77110
await createDirIfNotExists(outputDir, options)
78111

79-
log(options, `Writing to ${outputPath}`)
112+
log(options, LogLevel.Info, `Writing to ${outputPath}`)
80113
if (!exists || overwrite) {
81114
if (exists && overwrite) {
82-
log(options, `File ${outputPath} exists, overwriting`)
115+
log(options, LogLevel.Info, `File ${outputPath} exists, overwriting`)
83116
}
84117
const templateBuffer = await readFile(inputPath)
85118
const outputContents = handlebarsParse(templateBuffer, data)
86119

87120
if (!options.dryRun) {
88121
await writeFile(outputPath, outputContents)
89122
} else {
90-
log(options, "Content output:")
91-
log(options, outputContents)
123+
log(options, LogLevel.Info, "Content output:")
124+
log(options, LogLevel.Info, outputContents)
92125
}
93126
} else if (exists) {
94-
log(options, `File ${outputPath} already exists, skipping`)
127+
log(options, LogLevel.Info, `File ${outputPath} already exists, skipping`)
95128
}
96129
resolve()
97130
} catch (e: any) {

src/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
1+
export enum LogLevel {
2+
None = 0,
3+
Debug = 1,
4+
Info = 2,
5+
Warning = 3,
6+
Error = 4,
7+
}
8+
19
export type FileResponseFn<T> = (fullPath: string, basedir: string, basename: string) => T
210

311
export type FileResponse<T> = T | FileResponseFn<T>
412

513
export interface ScaffoldConfig {
614
/** The name supplied for the output templates */
715
name: string
16+
/** Template input files/dirs/glob patterns to use as template input. These will be copied to the output directory. */
817
templates: string[]
18+
/** Output directory to put scaffolded files in. */
919
output: FileResponse<string>
1020
createSubFolder?: boolean
1121
data?: Record<string, string>
1222
overwrite?: FileResponse<boolean>
1323
quiet?: boolean
24+
verbose?: LogLevel
1425
dryRun?: boolean
1526
}
1627
export interface ScaffoldCmdConfig {

src/utils.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import path from "path"
22
import { F_OK } from "constants"
3-
import { FileResponse, FileResponseFn, ScaffoldConfig } from "./types"
3+
import { FileResponse, FileResponseFn, LogLevel, ScaffoldConfig } from "./types"
44
import camelCase from "lodash/camelCase"
55
import snakeCase from "lodash/snakeCase"
66
import kebabCase from "lodash/kebabCase"
77
import startCase from "lodash/startCase"
88
import Handlebars from "handlebars"
99
import { promises as fsPromises } from "fs"
10+
import chalk from "chalk"
1011
const { stat, access, mkdir } = fsPromises
1112

1213
const helpers = {
@@ -26,11 +27,20 @@ export function handleErr(err: NodeJS.ErrnoException | null) {
2627
if (err) throw err
2728
}
2829

29-
export function log(options: ScaffoldConfig, ...obj: any[]) {
30-
if (options.quiet) {
30+
export function log(options: ScaffoldConfig, level: LogLevel, ...obj: any[]) {
31+
if (options.quiet || (options.verbose ?? LogLevel.Info) > level) {
3132
return
3233
}
33-
console["log"](...obj)
34+
const levelColor: Record<LogLevel, keyof typeof chalk> = {
35+
[LogLevel.None]: "reset",
36+
[LogLevel.Debug]: "blue",
37+
[LogLevel.Info]: "dim",
38+
[LogLevel.Warning]: "yellow",
39+
[LogLevel.Error]: "red",
40+
}
41+
const chalkFn: any = chalk[levelColor[level]]
42+
console["log"](...obj.map((i) => (typeof i === "object" ? chalkFn(JSON.stringify(i, undefined, 1)) : chalkFn(i))))
43+
// console["log"](...obj)
3444
}
3545

3646
export async function createDirIfNotExists(dir: string, options: ScaffoldConfig): Promise<void> {
@@ -95,3 +105,7 @@ export async function isDir(path: string): Promise<boolean> {
95105
const tplStat = await stat(path)
96106
return tplStat.isDirectory()
97107
}
108+
109+
export function removeGlob(template: string) {
110+
return template.replace(/\*/g, "").replace(/\/\//g, "/")
111+
}

0 commit comments

Comments
 (0)