fix: exclude globs

refactor: split main file iteration to 2 steps
This commit is contained in:
2024-09-16 21:56:33 +03:00
committed by Chen Asraf
parent 2c43dc4daf
commit 89dc43c73d
4 changed files with 69 additions and 34 deletions

View File

@@ -71,11 +71,10 @@ export function getBasePath(relPath: string): string {
.replace(process.cwd(), "")
}
export async function getFileList(_config: ScaffoldConfig, template: string): Promise<string[]> {
template = template.replaceAll(/[\\]+/g, "/")
log(_config, LogLevel.debug, `Getting file list for ${template}`)
export async function getFileList(config: ScaffoldConfig, templates: string[]): Promise<string[]> {
log(config, LogLevel.debug, `Getting file list for glob list: ${templates}`)
return (
await glob(template, {
await glob(templates, {
dot: true,
nodir: true,
})

View File

@@ -16,6 +16,7 @@ import {
getFileList,
getBasePath,
handleTemplateFile,
GlobInfo,
} from "./file"
import { LogLevel, MinimalConfig, Resolver, ScaffoldCmdConfig, ScaffoldConfig } from "./types"
import { registerHelpers } from "./parser"
@@ -61,39 +62,45 @@ export async function Scaffold(config: ScaffoldConfig): Promise<void> {
try {
config.data = { name: config.name, ...config.data }
logInitStep(config)
for (let _template of config.templates) {
const excludes = config.templates.filter((t) => t.startsWith("!"))
const includes = config.templates.filter((t) => !t.startsWith("!"))
const templates: GlobInfo[] = []
for (let _template of includes) {
try {
const { nonGlobTemplate, origTemplate, isDirOrGlob, isGlob, template } = await getTemplateGlobInfo(
config,
_template,
)
const files = await getFileList(config, template)
log(config, LogLevel.debug, "Iterating files", { files, template })
for (const inputFilePath of files) {
if (await isDir(inputFilePath)) {
continue
}
const relPath = makeRelativePath(path.dirname(removeGlob(inputFilePath).replace(nonGlobTemplate, "")))
const basePath = getBasePath(relPath)
logInputFile(config, {
originalTemplate: origTemplate,
relativePath: relPath,
parsedTemplate: template,
inputFilePath,
nonGlobTemplate,
basePath,
isDirOrGlob,
isGlob,
})
await handleTemplateFile(config, {
templatePath: inputFilePath,
basePath,
})
}
templates.push({ nonGlobTemplate, origTemplate, isDirOrGlob, isGlob, template })
} catch (e: any) {
handleErr(e)
}
}
for (const tpl of templates) {
const files = await getFileList(config, [tpl.template, ...excludes])
for (const file of files) {
if (await isDir(file)) {
continue
}
log(config, LogLevel.debug, "Iterating files", { files, file })
const relPath = makeRelativePath(path.dirname(removeGlob(file).replace(tpl.nonGlobTemplate, "")))
const basePath = getBasePath(relPath)
logInputFile(config, {
originalTemplate: tpl.origTemplate,
relativePath: relPath,
parsedTemplate: tpl.template,
inputFilePath: file,
nonGlobTemplate: tpl.nonGlobTemplate,
basePath,
isDirOrGlob: tpl.isDirOrGlob,
isGlob: tpl.isGlob,
})
await handleTemplateFile(config, {
templatePath: file,
basePath,
})
}
}
} catch (e: any) {
log(config, LogLevel.error, e)
throw e
@@ -111,7 +118,7 @@ export async function Scaffold(config: ScaffoldConfig): Promise<void> {
* @category Main
* @return {Promise<void>} A promise that resolves when the scaffold is complete
*/
Scaffold.fromConfig = async function (
Scaffold.fromConfig = async function(
/** The path or URL to the config file */
pathOrUrl: string,
/** Information needed before loading the config */

View File

@@ -22,6 +22,11 @@ export interface ScaffoldConfig {
* 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.
*
* You may omit files from output by prepending a `!` to their glob pattern.
*
* For example, `["components/**", "!components/README.md"]` will include everything in the directory `components`
* except the `README.md` file inside.
*
* @default Current working directory
*/
templates: string[]
@@ -331,7 +336,9 @@ export type FileResponse<T> = T | FileResponseHandler<T>
/**
* The Scaffold config for CLI
* Contains less and more specific options than {@link ScaffoldConfig}
* Contains less and more specific options than {@link ScaffoldConfig}.
*
* For more information about each option, see {@link ScaffoldConfig}.
*/
export type ScaffoldCmdConfig = {
/** The name of the scaffold template to use. */

View File

@@ -71,6 +71,14 @@ const fileStructDates = {
output: {},
}
const fileStructExcludes = {
input: {
"include.txt": "This file should be included",
"exclude.txt": "This file should be excluded",
},
output: {},
}
function withMock(fileStruct: FileSystem.DirectoryItems, testFn: jest.EmptyFunction): jest.EmptyFunction {
return () => {
beforeEach(() => {
@@ -268,8 +276,7 @@ describe("Scaffold", () => {
}),
)
describe(
"output structure",
describe("output structure", () => {
withMock(fileStructNested, () => {
test("should maintain input structure on output", async () => {
await Scaffold({
@@ -294,8 +301,23 @@ describe("Scaffold", () => {
expect(oneDeepFile.toString()).toEqual("Hello, my value is 1")
expect(twoDeepFile.toString()).toEqual("Hi! My value is actually NOT 1!")
})
}),
)
})
withMock(fileStructExcludes, () => {
test("should exclude files", async () => {
await Scaffold({
name: "app_name",
output: "output",
templates: ["input", "!exclude.txt"],
data: { value: "1" },
logLevel: "none",
})
const includeFile = readFileSync(join(process.cwd(), "output", "app_name.txt"))
expect(includeFile.toString()).toEqual("This file should be included")
expect(() => readFileSync(join(process.cwd(), "output", "exclude.txt"))).toThrow()
})
})
})
describe(
"capitalization helpers",