simple-scaffold 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/cmd.d.ts +2 -0
  2. package/cmd.js +127 -0
  3. package/cmd.js.map +1 -0
  4. package/index.d.ts +4 -0
  5. package/index.js +24 -0
  6. package/index.js.map +1 -0
  7. package/package.json +1 -4
  8. package/scaffold.d.ts +34 -0
  9. package/scaffold.js +113 -0
  10. package/scaffold.js.map +1 -0
  11. package/types.d.ts +310 -0
  12. package/types.js +32 -0
  13. package/types.js.map +1 -0
  14. package/utils.d.ts +66 -0
  15. package/utils.js +303 -0
  16. package/utils.js.map +1 -0
  17. package/.editorconfig +0 -8
  18. package/.github/FUNDING.yml +0 -13
  19. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -47
  20. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -24
  21. package/.github/workflows/docs.yml +0 -20
  22. package/.github/workflows/pull_requests.yml +0 -17
  23. package/.github/workflows/release.yml +0 -22
  24. package/.markdownlint.json +0 -9
  25. package/.prettierrc +0 -15
  26. package/.vscode/launch.json +0 -27
  27. package/.vscode/settings.json +0 -22
  28. package/.vscode/tasks.json +0 -59
  29. package/CHANGELOG.md +0 -111
  30. package/LICENSE +0 -21
  31. package/examples/test-input/Component/.hidden-file +0 -0
  32. package/examples/test-input/Component/button-example.png +0 -0
  33. package/examples/test-input/Component/inner/inner-{{name}}.txt +0 -1
  34. package/examples/test-input/Component/{{Name}}.tsx +0 -17
  35. package/examples/test-input/scaffold.config.js +0 -13
  36. package/jest.config.ts +0 -205
  37. package/media/intro.gif +0 -0
  38. package/pages/README.md +0 -7
  39. package/pages/cli.md +0 -74
  40. package/pages/configuration_files.md +0 -86
  41. package/pages/migration.md +0 -25
  42. package/pages/node.md +0 -53
  43. package/pages/templates.md +0 -224
  44. package/release.config.js +0 -76
  45. package/src/cmd.ts +0 -131
  46. package/src/docs.css +0 -55
  47. package/src/index.ts +0 -4
  48. package/src/scaffold.ts +0 -140
  49. package/src/types.ts +0 -342
  50. package/src/utils.ts +0 -423
  51. package/tests/scaffold.test.ts +0 -502
  52. package/tests/utils.test.ts +0 -124
  53. package/tsconfig.json +0 -16
  54. package/typedoc.config.js +0 -63
package/src/utils.ts DELETED
@@ -1,423 +0,0 @@
1
- import path from "path"
2
- import { F_OK } from "constants"
3
- import {
4
- DefaultHelpers,
5
- FileResponse,
6
- FileResponseHandler,
7
- Helper,
8
- LogLevel,
9
- ScaffoldCmdConfig,
10
- ScaffoldConfig,
11
- ScaffoldConfigFile,
12
- } from "./types"
13
- import camelCase from "lodash/camelCase"
14
- import snakeCase from "lodash/snakeCase"
15
- import kebabCase from "lodash/kebabCase"
16
- import startCase from "lodash/startCase"
17
- import Handlebars from "handlebars"
18
- import { promises as fsPromises } from "fs"
19
- import chalk from "chalk"
20
- const { stat, access, mkdir } = fsPromises
21
- import dtAdd from "date-fns/add"
22
- import dtFormat from "date-fns/format"
23
- import dtParseISO from "date-fns/parseISO"
24
- import { glob, hasMagic } from "glob"
25
- import { OptionsBase } from "massarg/types"
26
-
27
- const dateFns = {
28
- add: dtAdd,
29
- format: dtFormat,
30
- parseISO: dtParseISO,
31
- }
32
-
33
- const { readFile, writeFile } = fsPromises
34
-
35
- export const defaultHelpers: Record<DefaultHelpers, Helper> = {
36
- camelCase,
37
- snakeCase,
38
- startCase,
39
- kebabCase,
40
- hyphenCase: kebabCase,
41
- pascalCase,
42
- lowerCase: (text) => text.toLowerCase(),
43
- upperCase: (text) => text.toUpperCase(),
44
- now: nowHelper,
45
- date: dateHelper,
46
- }
47
-
48
- export function _dateHelper(date: Date, formatString: string): string
49
- export function _dateHelper(
50
- date: Date,
51
- formatString: string,
52
- durationDifference: number,
53
- durationType: keyof Duration,
54
- ): string
55
- export function _dateHelper(
56
- date: Date,
57
- formatString: string,
58
- durationDifference?: number,
59
- durationType?: keyof Duration,
60
- ): string {
61
- if (durationType && durationDifference !== undefined) {
62
- return dateFns.format(dateFns.add(date, { [durationType]: durationDifference }), formatString)
63
- }
64
- return dateFns.format(date, formatString)
65
- }
66
-
67
- export function nowHelper(formatString: string): string
68
- export function nowHelper(formatString: string, durationDifference: number, durationType: keyof Duration): string
69
- export function nowHelper(formatString: string, durationDifference?: number, durationType?: keyof Duration): string {
70
- return _dateHelper(new Date(), formatString, durationDifference!, durationType!)
71
- }
72
-
73
- export function dateHelper(date: string, formatString: string): string
74
- export function dateHelper(
75
- date: string,
76
- formatString: string,
77
- durationDifference: number,
78
- durationType: keyof Duration,
79
- ): string
80
-
81
- export function dateHelper(
82
- date: string,
83
- formatString: string,
84
- durationDifference?: number,
85
- durationType?: keyof Duration,
86
- ): string {
87
- return _dateHelper(dateFns.parseISO(date), formatString, durationDifference!, durationType!)
88
- }
89
-
90
- export function registerHelpers(config: ScaffoldConfig): void {
91
- const _helpers = { ...defaultHelpers, ...config.helpers }
92
- for (const helperName in _helpers) {
93
- log(config, LogLevel.Debug, `Registering helper: ${helperName}`)
94
- Handlebars.registerHelper(helperName, _helpers[helperName as keyof typeof _helpers])
95
- }
96
- }
97
-
98
- export function handleErr(err: NodeJS.ErrnoException | null): void {
99
- if (err) throw err
100
- }
101
-
102
- export function log(config: ScaffoldConfig, level: LogLevel, ...obj: any[]): void {
103
- if (config.quiet || config.verbose === LogLevel.None || level < (config.verbose ?? LogLevel.Info)) {
104
- return
105
- }
106
- const levelColor: Record<LogLevel, keyof typeof chalk> = {
107
- [LogLevel.None]: "reset",
108
- [LogLevel.Debug]: "blue",
109
- [LogLevel.Info]: "dim",
110
- [LogLevel.Warning]: "yellow",
111
- [LogLevel.Error]: "red",
112
- }
113
- const chalkFn: any = chalk[levelColor[level]]
114
- const key: "log" | "warn" | "error" = level === LogLevel.Error ? "error" : level === LogLevel.Warning ? "warn" : "log"
115
- const logFn: any = console[key]
116
- logFn(
117
- ...obj.map((i) =>
118
- i instanceof Error
119
- ? chalkFn(i, JSON.stringify(i, undefined, 1), i.stack)
120
- : typeof i === "object"
121
- ? chalkFn(JSON.stringify(i, undefined, 1))
122
- : chalkFn(i),
123
- ),
124
- )
125
- }
126
-
127
- export async function createDirIfNotExists(dir: string, config: ScaffoldConfig): Promise<void> {
128
- const parentDir = path.dirname(dir)
129
-
130
- if (!(await pathExists(parentDir))) {
131
- await createDirIfNotExists(parentDir, config)
132
- }
133
-
134
- if (!(await pathExists(dir))) {
135
- try {
136
- log(config, LogLevel.Debug, `Creating dir ${dir}`)
137
- await mkdir(dir)
138
- return
139
- } catch (e: any) {
140
- if (e.code !== "EEXIST") {
141
- throw e
142
- }
143
- return
144
- }
145
- }
146
- }
147
-
148
- export function getOptionValueForFile<T>(
149
- config: ScaffoldConfig,
150
- filePath: string,
151
- fn: FileResponse<T>,
152
- defaultValue?: T,
153
- ): T {
154
- if (typeof fn !== "function") {
155
- return defaultValue ?? (fn as T)
156
- }
157
- return (fn as FileResponseHandler<T>)(
158
- filePath,
159
- path.dirname(handlebarsParse(config, filePath, { isPath: true }).toString()),
160
- path.basename(handlebarsParse(config, filePath, { isPath: true }).toString()),
161
- )
162
- }
163
-
164
- export function handlebarsParse(
165
- config: ScaffoldConfig,
166
- templateBuffer: Buffer | string,
167
- { isPath = false }: { isPath?: boolean } = {},
168
- ): Buffer {
169
- const { data } = config
170
- try {
171
- let str = templateBuffer.toString()
172
- if (isPath) {
173
- str = str.replace(/\\/g, "/")
174
- }
175
- const parser = Handlebars.compile(str, { noEscape: true })
176
- let outputContents = parser(data)
177
- if (isPath && path.sep !== "/") {
178
- outputContents = outputContents.replace(/\//g, "\\")
179
- }
180
- return Buffer.from(outputContents)
181
- } catch (e) {
182
- log(config, LogLevel.Debug, e)
183
- log(config, LogLevel.Warning, "Couldn't parse file with handlebars, returning original content")
184
- return Buffer.from(templateBuffer)
185
- }
186
- }
187
-
188
- export async function pathExists(filePath: string): Promise<boolean> {
189
- try {
190
- await access(filePath, F_OK)
191
- return true
192
- } catch (e: any) {
193
- if (e.code === "ENOENT") {
194
- return false
195
- }
196
- throw e
197
- }
198
- }
199
-
200
- export function pascalCase(s: string): string {
201
- return startCase(s).replace(/\s+/g, "")
202
- }
203
-
204
- export async function isDir(path: string): Promise<boolean> {
205
- const tplStat = await stat(path)
206
- return tplStat.isDirectory()
207
- }
208
-
209
- export function removeGlob(template: string): string {
210
- return template.replace(/\*/g, "").replace(/(\/\/|\\\\)/g, path.sep)
211
- }
212
-
213
- export function makeRelativePath(str: string): string {
214
- return str.startsWith(path.sep) ? str.slice(1) : str
215
- }
216
-
217
- export function getBasePath(relPath: string): string {
218
- return path
219
- .resolve(process.cwd(), relPath)
220
- .replace(process.cwd() + path.sep, "")
221
- .replace(process.cwd(), "")
222
- }
223
-
224
- export async function getFileList(config: ScaffoldConfig, template: string): Promise<string[]> {
225
- return (
226
- await glob(template, {
227
- dot: true,
228
- nodir: true,
229
- // debug: config.verbose === LogLevel.Debug,
230
- })
231
- ).map((f) => f.replace(/\//g, path.sep))
232
- }
233
-
234
- export interface GlobInfo {
235
- nonGlobTemplate: string
236
- origTemplate: string
237
- isDirOrGlob: boolean
238
- isGlob: boolean
239
- template: string
240
- }
241
-
242
- export async function getTemplateGlobInfo(config: ScaffoldConfig, template: string): Promise<GlobInfo> {
243
- const isGlob = hasMagic(template)
244
- log(config, LogLevel.Debug, "before isDir", "isGlob:", isGlob, template)
245
- let _template = template
246
- let nonGlobTemplate = isGlob ? removeGlob(template) : template
247
- nonGlobTemplate = path.normalize(nonGlobTemplate)
248
- const isDirOrGlob = isGlob ? true : await isDir(template)
249
- log(config, LogLevel.Debug, "after isDir", isDirOrGlob)
250
- const _shouldAddGlob = !isGlob && isDirOrGlob
251
- const origTemplate = template
252
- if (_shouldAddGlob) {
253
- _template = path.join(template, "**", "*")
254
- }
255
- return { nonGlobTemplate, origTemplate, isDirOrGlob, isGlob, template: _template }
256
- }
257
-
258
- export interface OutputFileInfo {
259
- inputPath: string
260
- outputPathOpt: string
261
- outputDir: string
262
- outputPath: string
263
- exists: boolean
264
- }
265
-
266
- export async function getTemplateFileInfo(
267
- config: ScaffoldConfig,
268
- { templatePath, basePath }: { templatePath: string; basePath: string },
269
- ): Promise<OutputFileInfo> {
270
- const inputPath = path.resolve(process.cwd(), templatePath)
271
- const outputPathOpt = getOptionValueForFile(config, inputPath, config.output)
272
- const outputDir = getOutputDir(config, outputPathOpt, basePath)
273
- const outputPath = handlebarsParse(config, path.join(outputDir, path.basename(inputPath)), {
274
- isPath: true,
275
- }).toString()
276
- const exists = await pathExists(outputPath)
277
- return { inputPath, outputPathOpt, outputDir, outputPath, exists }
278
- }
279
-
280
- export async function copyFileTransformed(
281
- config: ScaffoldConfig,
282
- {
283
- exists,
284
- overwrite,
285
- outputPath,
286
- inputPath,
287
- }: {
288
- exists: boolean
289
- overwrite: boolean
290
- outputPath: string
291
- inputPath: string
292
- },
293
- ): Promise<void> {
294
- if (!exists || overwrite) {
295
- if (exists && overwrite) {
296
- log(config, LogLevel.Info, `File ${outputPath} exists, overwriting`)
297
- }
298
- const templateBuffer = await readFile(inputPath)
299
- const unprocessedOutputContents = handlebarsParse(config, templateBuffer)
300
- const finalOutputContents =
301
- (await config.beforeWrite?.(unprocessedOutputContents, templateBuffer, outputPath)) ?? unprocessedOutputContents
302
-
303
- if (!config.dryRun) {
304
- await writeFile(outputPath, finalOutputContents)
305
- log(config, LogLevel.Info, "Done.")
306
- } else {
307
- log(config, LogLevel.Info, "Content output:")
308
- log(config, LogLevel.Info, finalOutputContents)
309
- }
310
- } else if (exists) {
311
- log(config, LogLevel.Info, `File ${outputPath} already exists, skipping`)
312
- }
313
- }
314
-
315
- export function getOutputDir(config: ScaffoldConfig, outputPathOpt: string, basePath: string): string {
316
- return path.resolve(
317
- process.cwd(),
318
- ...([
319
- outputPathOpt,
320
- basePath,
321
- config.createSubFolder
322
- ? config.subFolderNameHelper
323
- ? handlebarsParse(config, `{{ ${config.subFolderNameHelper} name }}`).toString()
324
- : config.name
325
- : undefined,
326
- ].filter(Boolean) as string[]),
327
- )
328
- }
329
-
330
- export function logInputFile(
331
- config: ScaffoldConfig,
332
- {
333
- origTemplate,
334
- relPath,
335
- template,
336
- inputFilePath,
337
- nonGlobTemplate,
338
- basePath,
339
- isDirOrGlob,
340
- isGlob,
341
- }: {
342
- origTemplate: string
343
- relPath: string
344
- template: string
345
- inputFilePath: string
346
- nonGlobTemplate: string
347
- basePath: string
348
- isDirOrGlob: boolean
349
- isGlob: boolean
350
- },
351
- ): void {
352
- log(
353
- config,
354
- LogLevel.Debug,
355
- `\nprocess.cwd(): ${process.cwd()}`,
356
- `\norigTemplate: ${origTemplate}`,
357
- `\nrelPath: ${relPath}`,
358
- `\ntemplate: ${template}`,
359
- `\ninputFilePath: ${inputFilePath}`,
360
- `\nnonGlobTemplate: ${nonGlobTemplate}`,
361
- `\nbasePath: ${basePath}`,
362
- `\nisDirOrGlob: ${isDirOrGlob}`,
363
- `\nisGlob: ${isGlob}`,
364
- `\n`,
365
- )
366
- }
367
-
368
- export function logInitStep(config: ScaffoldConfig): void {
369
- log(config, LogLevel.Debug, "Full config:", {
370
- name: config.name,
371
- templates: config.templates,
372
- output: config.output,
373
- createSubfolder: config.createSubFolder,
374
- data: config.data,
375
- overwrite: config.overwrite,
376
- quiet: config.quiet,
377
- subFolderTransformHelper: config.subFolderNameHelper,
378
- helpers: Object.keys(config.helpers ?? {}),
379
- verbose: `${config.verbose} (${Object.keys(LogLevel).find(
380
- (k) => (LogLevel[k as any] as unknown as number) === config.verbose!,
381
- )})`,
382
- })
383
- log(config, LogLevel.Info, "Data:", config.data)
384
- }
385
-
386
- export function parseAppendData(value: string, options: ScaffoldCmdConfig & OptionsBase): unknown {
387
- const data = options.data ?? {}
388
- const [key, val] = value.split(/\:?=/)
389
- // raw
390
- if (value.includes(":=") && !val.includes(":=")) {
391
- return { ...data, [key]: JSON.parse(val) }
392
- }
393
- return { ...data, [key]: isWrappedWithQuotes(val) ? val.substring(1, val.length - 1) : val }
394
- }
395
-
396
- function isWrappedWithQuotes(string: string): boolean {
397
- return (string.startsWith('"') && string.endsWith('"')) || (string.startsWith("'") && string.endsWith("'"))
398
- }
399
-
400
- /** @internal */
401
- export function parseConfig(config: ScaffoldCmdConfig & OptionsBase): ScaffoldConfig {
402
- let c: ScaffoldConfig = config
403
-
404
- if (config.config) {
405
- const [configFile, template = "default"] = config.config.split(":")
406
- const configImport: ScaffoldConfigFile = require(path.resolve(process.cwd(), configFile))
407
- if (!configImport[template]) {
408
- throw new Error(`Template "${template}" not found in ${configFile}`)
409
- }
410
- c = {
411
- ...config,
412
- ...configImport[template],
413
- data: {
414
- ...configImport[template].data,
415
- ...config.data,
416
- },
417
- }
418
- }
419
-
420
- c.data = { ...c.data, ...config.appendData }
421
- delete config.appendData
422
- return c
423
- }