migrate cmd to massarg + update tests

This commit is contained in:
Chen Asraf
2021-09-26 11:02:16 +03:00
parent aeddd44778
commit a52f9a0b84
9 changed files with 156 additions and 72 deletions

8
.vscode/tasks.json vendored
View File

@@ -25,6 +25,12 @@
"type": "npm",
"problemMatcher": [],
},
{
"command": "yarn test --watchAll",
"label": "yarn test --watchAll",
"type": "shell",
"problemMatcher": [],
},
{
"script": "cmd",
"label": "cmd",
@@ -44,4 +50,4 @@
"problemMatcher": [],
},
],
}
}

View File

@@ -25,6 +25,7 @@
"glob": "^7.1.3",
"handlebars": "^4.7.7",
"lodash": "^4.17.21",
"massarg": "^0.1.2",
"util.promisify": "^1.1.1"
},
"devDependencies": {

View File

@@ -1,60 +1,88 @@
import Scaffold from "./scaffold"
import args from "args"
import massarg from "massarg"
import { ScaffoldCmdConfig } from "./types"
const options = args
.command("", "")
.option(
["n", "name"],
"Name to be passed to the generated files. {{name}} and {{Name}} inside contents and file names will be replaced accordingly."
)
.option(
["o", "output"],
"Path to output to. If --create-sub-folder is enabled, the subfolder will be created inside this path."
)
.option(
["t", "templates"],
"Template files to use as input. You may provide multiple files, each of which can be a relative or absolute path, " +
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.",
[]
)
.option(["w", "overwrite"], "Enable to override output files, even if they already exist.", false)
.option(
["d", "data"],
"Add custom data to the templates. By default, only your app name is included.",
undefined,
JSON.parse
)
.option(["s", "create-sub-folder"], "Create subfolder with the input name", false)
.option(["q", "silent"], "Supress output logs", false)
.option(
["d", "dry-run"],
"Don't emit actual files. This is good for testing your scaffolds and making sure they " +
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: "Supress output logs", defaultValue: false, boolean: true })
.option({
aliases: ["dr"],
name: "dry-run",
description:
"Don't emit actual files. This is good for testing your scaffolds and making sure they " +
"don't fail, without having to write actual files.",
false
)
.example(
`yarn cmd -t examples/test-input/Component -o examples/test-output -d '{"property":"myProp","value":"10"}'`,
"Usage"
)
.parse(process.argv, {
value: "name",
mri: {},
mainColor: "yellow",
subColor: ["dim"],
name: "simple-scaffold",
usageFilter: (usage: string) => {
usage = usage.replace("[options] [command] ", "").replace("name", "[options] name")
const lines = usage.split("\n")
usage = [
...lines.slice(
0,
lines.findIndex((l) => l.startsWith(" Commands:"))
),
...lines.slice(lines.findIndex((l) => l.startsWith(" Options:"))),
].join("\n")
return usage
},
}) as ScaffoldCmdConfig
Scaffold(options)
defaultValue: false,
boolean: true,
})
// .example(
// `yarn cmd -t examples/test-input/Component -o examples/test-output -d '{"property":"myProp","value":"10"}'`,
// "Usage"
// )
.help({
binName: "simple-scaffold",
useGlobalColumns: true,
usageExample: "[options]",
})
.parse()
// .parse(process.argv, {
// value: "name",
// mri: {},
// mainColor: "yellow",
// subColor: ["dim"],
// name: "simple-scaffold",
// usageFilter: (usage: string) => {
// usage = usage.replace("[options] [command] ", "").replace("name", "[options] name")
// const lines = usage.split("\n")
// usage = [
// ...lines.slice(
// 0,
// lines.findIndex((l) => l.startsWith(" Commands:"))
// ),
// ...lines.slice(lines.findIndex((l) => l.startsWith(" Options:"))),
// ].join("\n")
// return usage
// },
// }
// ) as ScaffoldCmdConfig

View File

@@ -25,15 +25,21 @@ export async function Scaffold(config: ScaffoldConfig) {
createSubfolder: options.createSubFolder,
data: options.data,
overwrite: options.overwrite,
silent: options.silent,
quiet: options.quiet,
})
log(options, "Data:", data)
for (let template of config.templates) {
try {
// try {
const tplStat = await stat(template)
if (tplStat.isDirectory()) {
template = template + "/**/*"
}
// } catch (e) {
// if (e.code !== "ENOENT") {
// throw e
// }
// }
const files = await promisify(glob)(template, { dot: true })
for (const templatePath of files) {
await handleTemplateFile(templatePath, options, data)

4
src/types.d.ts vendored
View File

@@ -9,7 +9,7 @@ export interface ScaffoldConfig {
createSubFolder?: boolean
data?: Record<string, string>
overwrite?: FileResponse<boolean>
silent?: boolean
quiet?: boolean
dryRun?: boolean
}
export interface ScaffoldCmdConfig {
@@ -19,6 +19,6 @@ export interface ScaffoldCmdConfig {
createSubFolder: boolean
data?: Record<string, string>
overwrite: boolean
silent: boolean
quiet: boolean
dryRun: boolean
}

View File

@@ -26,7 +26,7 @@ export function handleErr(err: NodeJS.ErrnoException | null) {
}
export function log(options: ScaffoldConfig, ...obj: any[]) {
if (options.silent) {
if (options.quiet) {
return
}
console.log(...obj)

View File

@@ -1,6 +1,6 @@
import mockFs from "mock-fs"
import Scaffold from "../scaffold"
import { readFileSync } from "fs"
import Scaffold from "../src/scaffold"
import { readdirSync, readFileSync } from "fs"
const fileStructNormal = {
input: {
@@ -16,6 +16,16 @@ const fileStructWithData = {
output: {},
}
const fileStructNested = {
input: {
"{{name}}-1.text": "This should be in root",
"{{Name}}": {
"{{name}}-2.txt": "Hello, my value is {{value}}",
},
},
output: {},
}
describe("Scaffold", () => {
describe("create subfolder", () => {
beforeAll(() => {
@@ -28,7 +38,7 @@ describe("Scaffold", () => {
name: "app_name",
output: "output",
templates: ["input"],
silent: true,
quiet: true,
})
const data = readFileSync(process.cwd() + "/output/app_name.txt")
@@ -41,7 +51,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
createSubFolder: true,
silent: true,
quiet: true,
})
const data = readFileSync(process.cwd() + "/output/app_name/app_name.txt")
@@ -63,7 +73,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "1" },
silent: true,
quiet: true,
})
await Scaffold({
@@ -71,7 +81,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "2" },
silent: true,
quiet: true,
})
const data = readFileSync(process.cwd() + "/output/app_name.txt")
@@ -84,7 +94,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["input"],
data: { value: "1" },
silent: true,
quiet: true,
})
await Scaffold({
@@ -93,7 +103,7 @@ describe("Scaffold", () => {
templates: ["input"],
data: { value: "2" },
overwrite: true,
silent: true,
quiet: true,
})
const data = readFileSync(process.cwd() + "/output/app_name.txt")
@@ -118,7 +128,7 @@ describe("Scaffold", () => {
output: "output",
templates: ["non-existing-input"],
data: { value: "1" },
silent: true,
quiet: true,
})
).rejects.toThrow()
@@ -143,7 +153,7 @@ describe("Scaffold", () => {
output: (fullPath, basedir, basename) => `custom-output/${basename.split(".")[0]}`,
templates: ["input"],
data: { value: "1" },
silent: true,
quiet: true,
})
const data = readFileSync(process.cwd() + "/custom-output/app_name/app_name.txt")
expect(data.toString()).toBe("Hello, my app is app_name")
@@ -153,4 +163,29 @@ describe("Scaffold", () => {
mockFs.restore()
})
})
describe("output structure", () => {
beforeAll(() => {
mockFs.restore()
mockFs(fileStructNested)
})
test("should maintain input structure on output", async () => {
await Scaffold({
name: "app_name",
output: "./",
templates: ["input"],
data: { value: "1" },
quiet: true,
})
const dir = readdirSync(process.cwd())
console.log({ dir })
expect(dir).toHaveProperty("length")
})
afterAll(() => {
mockFs.restore()
})
})
})

View File

@@ -17,6 +17,6 @@
"src/cmd.ts"
],
"exclude": [
"src/tests/*"
"tests/*"
]
}

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.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==
@@ -2153,6 +2153,14 @@ makeerror@1.0.x:
dependencies:
tmpl "1.0.x"
massarg@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/massarg/-/massarg-0.1.2.tgz#f298741318172be14f3d2b701329fbe10eff41bb"
integrity sha512-gpFIjsvOoqyQnrqNDytQXPljOGlX5lvJFGYzAIqjxDqiSZwHOvz+/YfjtzrFvokfYsk0uZbE/XOH4LVRiu/1cg==
dependencies:
chalk "^4.1.1"
lodash "^4.17.21"
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"