Skip to content

Commit f25cda7

Browse files
committed
feat: try multiple default config files
1 parent 995b433 commit f25cda7

File tree

3 files changed

+105
-22
lines changed

3 files changed

+105
-22
lines changed

jest.config.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ export default {
2626
coverageDirectory: "coverage",
2727

2828
// An array of regexp pattern strings used to skip coverage collection
29-
// coveragePathIgnorePatterns: [
30-
// "/node_modules/"
31-
// ],
29+
coveragePathIgnorePatterns: ["/node_modules/", "scaffold.config.js"],
3230

3331
// Indicates which provider should be used to instrument code for coverage
3432
coverageProvider: "v8",

src/git.ts

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import path from "node:path"
22
import os from "node:os"
3+
import fs from "node:fs/promises"
34
import { log } from "./logger"
45
import { AsyncResolver, LogConfig, LogLevel, ScaffoldCmdConfig, ScaffoldConfigMap } from "./types"
56
import { spawn } from "node:child_process"
@@ -22,29 +23,60 @@ export async function getGitConfig(
2223
clone.on("error", reject)
2324
clone.on("close", async (code) => {
2425
if (code === 0) {
25-
log(logConfig, LogLevel.info, `Loading config from git repo: ${repoUrl}`)
26-
// TODO search for dynamic config file in repo if not provided
27-
const filename = file || "scaffold.config.js"
28-
const absolutePath = path.resolve(tmpPath, filename)
29-
const loadedConfig = await resolve(
30-
async () => (await import(absolutePath)).default as ScaffoldConfigMap,
31-
logConfig,
32-
)
33-
34-
log(logConfig, LogLevel.info, `Loaded config from git`)
35-
log(logConfig, LogLevel.debug, `Raw config:`, loadedConfig)
36-
const fixedConfig: ScaffoldConfigMap = {}
37-
for (const [k, v] of Object.entries(loadedConfig)) {
38-
fixedConfig[k] = {
39-
...v,
40-
templates: v.templates.map((t) => path.resolve(tmpPath, t)),
41-
}
42-
}
43-
res(wrapNoopResolver(fixedConfig))
26+
res(await loadGitConfig({ logConfig, url: repoUrl, file, tmpPath }))
4427
return
4528
}
4629

4730
reject(new Error(`Git clone failed with code ${code}`))
4831
})
4932
})
5033
}
34+
35+
/** @internal */
36+
export async function loadGitConfig({
37+
logConfig,
38+
url: repoUrl,
39+
file,
40+
tmpPath,
41+
}: {
42+
logConfig: LogConfig
43+
url: string
44+
file: string
45+
tmpPath: string
46+
}): Promise<AsyncResolver<ScaffoldCmdConfig, ScaffoldConfigMap>> {
47+
log(logConfig, LogLevel.info, `Loading config from git repo: ${repoUrl}`)
48+
const filename = file || (await findConfigFile(tmpPath))
49+
const absolutePath = path.resolve(tmpPath, filename)
50+
const loadedConfig = await resolve(async () => (await import(absolutePath)).default as ScaffoldConfigMap, logConfig)
51+
52+
log(logConfig, LogLevel.info, `Loaded config from git`)
53+
log(logConfig, LogLevel.debug, `Raw config:`, loadedConfig)
54+
const fixedConfig: ScaffoldConfigMap = {}
55+
for (const [k, v] of Object.entries(loadedConfig)) {
56+
fixedConfig[k] = {
57+
...v,
58+
templates: v.templates.map((t) => path.resolve(tmpPath, t)),
59+
}
60+
}
61+
await fs.rm(tmpPath, { recursive: true })
62+
return wrapNoopResolver(fixedConfig)
63+
}
64+
65+
/** @internal */
66+
export async function findConfigFile(root: string): Promise<string> {
67+
const allowed = ["mjs", "cjs", "js", "json"].reduce((acc, ext) => {
68+
acc.push(`scaffold.config.${ext}`)
69+
acc.push(`scaffold.${ext}`)
70+
return acc
71+
}, [] as string[])
72+
for (const file of allowed) {
73+
const exists = await fs
74+
.stat(path.resolve(root, file))
75+
.then(() => true)
76+
.catch(() => false)
77+
if (exists) {
78+
return file
79+
}
80+
}
81+
throw new Error(`Could not find config file in git repo`)
82+
}

tests/config.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
import mockFs from "mock-fs"
2+
import FileSystem from "mock-fs/lib/filesystem"
3+
import { Console } from "console"
14
import { LogLevel, ScaffoldCmdConfig } from "../src/types"
25
import * as config from "../src/config"
36
import { resolve } from "../src/utils"
47
// @ts-ignore
58
import * as configFile from "../scaffold.config"
9+
import { findConfigFile } from "../src/git"
610

711
jest.mock("../src/git", () => {
812
return {
@@ -28,6 +32,11 @@ const blankCliConf: ScaffoldCmdConfig = {
2832
quiet: false,
2933
}
3034

35+
const blankConfig: ScaffoldCmdConfig = {
36+
...blankCliConf,
37+
data: {},
38+
}
39+
3140
describe("config", () => {
3241
describe("parseAppendData", () => {
3342
test('works for "key=value"', () => {
@@ -102,4 +111,48 @@ describe("config", () => {
102111
expect(result).toEqual(configFile)
103112
})
104113
})
114+
115+
describe("findConfigFile", () => {
116+
const struct1 = {
117+
"scaffold.config.js": `module.exports = '${JSON.stringify(blankConfig)}'`,
118+
}
119+
const struct2 = {
120+
"scaffold.js": `module.exports = '${JSON.stringify(blankConfig)}'`,
121+
}
122+
const struct3 = {
123+
"scaffold.cjs": `module.exports = '${JSON.stringify(blankConfig)}'`,
124+
}
125+
const struct4 = {
126+
"scaffold.json": JSON.stringify(blankConfig),
127+
}
128+
129+
function withMock(fileStruct: FileSystem.DirectoryItems, testFn: jest.EmptyFunction): jest.EmptyFunction {
130+
return () => {
131+
beforeEach(() => {
132+
// console.log("Mocking:", fileStruct)
133+
console = new Console(process.stdout, process.stderr)
134+
135+
mockFs(fileStruct)
136+
// logMock = jest.spyOn(console, 'log').mockImplementation((...args) => {
137+
// logsTemp.push(args)
138+
// })
139+
})
140+
testFn()
141+
afterEach(() => {
142+
// console.log("Restoring mock")
143+
mockFs.restore()
144+
})
145+
}
146+
}
147+
148+
for (const struct of [struct1, struct2, struct3, struct4]) {
149+
const [k] = Object.keys(struct)
150+
describe(`finds config file ${k}`, () => {
151+
withMock(struct, async () => {
152+
const result = await findConfigFile(process.cwd())
153+
expect(result).toEqual(k)
154+
})
155+
})
156+
}
157+
})
105158
})

0 commit comments

Comments
 (0)