Skip to content

Commit 8672e2c

Browse files
authored
Merge pull request #56 from EmbeddedEnterprises/build
fix: do not require a cmake-ts key + tests for config parsing
2 parents bb46580 + 7e3ecfb commit 8672e2c

File tree

4 files changed

+232
-9
lines changed

4 files changed

+232
-9
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ coverage/
1010
.DS_Store
1111
html/
1212
*.tsbuildinfo
13+
test/package-test.json

src/build.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export async function build(opts: Options): Promise<BuildConfiguration[] | null>
2727
return []
2828
}
2929

30-
const configFile = await getConfigFile()
30+
const packageJsonPath = resolve(join(process.cwd(), "package.json"))
31+
const configFile = await getConfigFile(packageJsonPath)
3132
if (configFile instanceof Error) {
3233
logger.error(configFile)
3334
return null

src/config.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { join, resolve } from "path"
21
import { readJson } from "fs-extra"
32
import which from "which"
43
import type { BuildCommandOptions, BuildConfiguration, BuildConfigurations, Options } from "./config-types.d"
54
import { getCmakeGenerator } from "./generator.js"
5+
import { logger } from "./lib.js"
66

77
export async function parseBuildConfigs(
88
opts: Options,
@@ -51,7 +51,7 @@ export async function parseBuildConfigs(
5151
/**
5252
* Add the missing fields to the given build configuration.
5353
*/
54-
async function getBuildConfig(
54+
export async function getBuildConfig(
5555
buildOptions: BuildCommandOptions,
5656
config: Partial<BuildConfiguration>,
5757
globalConfig: Partial<BuildConfigurations>,
@@ -111,7 +111,7 @@ async function getBuildConfig(
111111
return config as BuildConfiguration
112112
}
113113

114-
function parseBuiltInConfigs(configName: string) {
114+
export function parseBuiltInConfigs(configName: string) {
115115
const parts = configName.split("-")
116116

117117
let cross = false
@@ -181,19 +181,19 @@ const buildTypes = new Map<string, BuildConfiguration["buildType"]>([
181181

182182
const runtimes = new Set<BuildConfiguration["runtime"]>(["node", "electron", "iojs"])
183183

184-
export async function getConfigFile() {
184+
export async function getConfigFile(packageJsonPath: string) {
185185
let packJson: { "cmake-ts": Partial<BuildConfigurations> | undefined } & Record<string, unknown>
186186
try {
187-
// TODO getting the path from the CLI
188-
const packageJsonPath = resolve(join(process.cwd(), "package.json"))
189187
packJson = await readJson(packageJsonPath)
190188
} catch (err) {
191-
return new Error(`Failed to load package.json, maybe your cwd is wrong ${err}`)
189+
logger.warn(`Failed to load package.json at ${packageJsonPath}: ${err}. Using defaults.`)
190+
return {}
192191
}
193192

194193
const configFile = packJson["cmake-ts"]
195194
if (configFile === undefined) {
196-
return new Error("Package.json does not have cmake-ts key defined!")
195+
logger.debug("Package.json does not have cmake-ts key defined. Using defaults.")
196+
return {}
197197
}
198198

199199
return configFile

test/config.test.ts

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import { join } from "path"
2+
import { writeJson } from "fs-extra"
3+
import { afterEach, beforeEach, expect, suite, test } from "vitest"
4+
import type { BuildCommandOptions, BuildConfiguration, BuildConfigurations, Options } from "../src/config-types.d.js"
5+
import { getBuildConfig, getConfigFile, parseBuildConfigs, parseBuiltInConfigs } from "../src/config.js"
6+
7+
suite("Config Functions", () => {
8+
const mockBuildOptions: BuildCommandOptions = {
9+
configs: [],
10+
addonSubdirectory: "",
11+
packageDirectory: process.cwd(),
12+
projectName: "test-project",
13+
targetDirectory: "build",
14+
stagingDirectory: "staging",
15+
help: false,
16+
}
17+
18+
const mockConfigFile: Partial<BuildConfigurations> = {
19+
name: "test-config",
20+
configurations: [
21+
{
22+
name: "linux-x64",
23+
os: "linux",
24+
arch: "x64",
25+
},
26+
{
27+
name: "windows-x64",
28+
os: "win32",
29+
arch: "x64",
30+
},
31+
],
32+
}
33+
34+
const testPackageJsonPath = join(process.cwd(), "test", "package-test.json")
35+
36+
beforeEach(async () => {
37+
// Reset environment variables
38+
process.env.npm_config_target_arch = undefined
39+
process.env.npm_config_target_os = undefined
40+
41+
// Create test package.json
42+
await writeJson(testPackageJsonPath, {
43+
name: "test-package",
44+
version: "1.0.0",
45+
"cmake-ts": mockConfigFile,
46+
})
47+
})
48+
49+
afterEach(async () => {
50+
// Clean up environment variables
51+
process.env.npm_config_target_arch = undefined
52+
process.env.npm_config_target_os = undefined
53+
54+
// Remove test package.json
55+
try {
56+
await writeJson(testPackageJsonPath, {})
57+
} catch (err) {
58+
// Ignore errors if file doesn't exist
59+
}
60+
})
61+
62+
suite("parseBuildConfigs", () => {
63+
test("should return null for non-build commands", async () => {
64+
const result = await parseBuildConfigs(
65+
{ command: { type: "none" }, logger: "info", help: false } as Options,
66+
mockConfigFile,
67+
)
68+
expect(result).toBeNull()
69+
})
70+
71+
test("should build for current runtime/system when no configs specified", async () => {
72+
const result = await parseBuildConfigs(
73+
{ command: { type: "build", options: mockBuildOptions }, logger: "info", help: false } as Options,
74+
mockConfigFile,
75+
)
76+
expect(result).toHaveLength(1)
77+
expect(result![0].os).toBe(process.platform)
78+
expect(result![0].arch).toBe(process.arch)
79+
})
80+
81+
test("should build specified named configs", async () => {
82+
const options = { ...mockBuildOptions, configs: ["linux-x64"] }
83+
const result = await parseBuildConfigs(
84+
{ command: { type: "build", options }, logger: "info", help: false } as Options,
85+
mockConfigFile,
86+
)
87+
expect(result).toHaveLength(1)
88+
expect(result![0].name).toBe("linux-x64")
89+
expect(result![0].os).toBe("linux")
90+
expect(result![0].arch).toBe("x64")
91+
})
92+
93+
test("should use default values when no config file is provided", async () => {
94+
const result = await parseBuildConfigs(
95+
{ command: { type: "build", options: mockBuildOptions }, logger: "info", help: false } as Options,
96+
{},
97+
)
98+
expect(result).toHaveLength(1)
99+
expect(result![0].os).toBe(process.platform)
100+
expect(result![0].arch).toBe(process.arch)
101+
expect(result![0].runtime).toBe("node")
102+
expect(result![0].buildType).toBe("Release")
103+
expect(result![0].dev).toBe(false)
104+
})
105+
})
106+
107+
suite("getBuildConfig", () => {
108+
test("should merge configs correctly", async () => {
109+
const partialConfig: Partial<BuildConfiguration> = {
110+
name: "test-config",
111+
os: "linux",
112+
}
113+
114+
const result = await getBuildConfig(mockBuildOptions, partialConfig, mockConfigFile)
115+
116+
expect(result.name).toBe("test-config")
117+
expect(result.os).toBe("linux")
118+
expect(result.arch).toBe(process.arch)
119+
expect(result.runtime).toBe("node")
120+
expect(result.buildType).toBe("Release")
121+
})
122+
123+
test("should handle cross compilation flags", async () => {
124+
const partialConfig: Partial<BuildConfiguration> = {
125+
os: "win32",
126+
arch: "x64",
127+
}
128+
129+
const result = await getBuildConfig(mockBuildOptions, partialConfig, mockConfigFile)
130+
131+
expect(result.cross).toBe(true)
132+
})
133+
134+
test("should set cross flag when npm_config_target_arch differs from process.arch", async () => {
135+
// Set npm_config_target_arch to a different architecture than the current one
136+
process.env.npm_config_target_arch = process.arch === "x64" ? "arm64" : "x64"
137+
138+
const partialConfig: Partial<BuildConfiguration> = {
139+
os: process.platform,
140+
arch: process.env.npm_config_target_arch as NodeJS.Architecture,
141+
}
142+
143+
const result = await getBuildConfig(mockBuildOptions, partialConfig, mockConfigFile)
144+
145+
expect(result.cross).toBe(true)
146+
expect(result.arch).toBe(process.env.npm_config_target_arch)
147+
})
148+
149+
test("should set cross flag when npm_config_target_arch differs from process.arch when no config file is provided", async () => {
150+
// Set npm_config_target_arch to a different architecture than the current one
151+
process.env.npm_config_target_arch = process.arch === "x64" ? "arm64" : "x64"
152+
153+
const partialConfig: Partial<BuildConfiguration> = {
154+
os: process.platform,
155+
arch: process.env.npm_config_target_arch as NodeJS.Architecture,
156+
}
157+
158+
const result = await getBuildConfig(mockBuildOptions, partialConfig, {})
159+
160+
expect(result.cross).toBe(true)
161+
expect(result.arch).toBe(process.env.npm_config_target_arch)
162+
})
163+
164+
test("should use default values when no config file is provided", async () => {
165+
const partialConfig: Partial<BuildConfiguration> = {
166+
name: "test-config",
167+
}
168+
169+
const result = await getBuildConfig(mockBuildOptions, partialConfig, {})
170+
171+
expect(result.name).toBe("test-config")
172+
expect(result.os).toBe(process.platform)
173+
expect(result.arch).toBe(process.arch)
174+
expect(result.runtime).toBe("node")
175+
expect(result.buildType).toBe("Release")
176+
expect(result.dev).toBe(false)
177+
})
178+
})
179+
180+
suite("parseBuiltInConfigs", () => {
181+
test("should parse valid config names", () => {
182+
const result = parseBuiltInConfigs("linux-x64-node-release")
183+
expect(result.os).toBe("linux")
184+
expect(result.arch).toBe("x64")
185+
expect(result.runtime).toBe("node")
186+
expect(result.buildType).toBe("Release")
187+
})
188+
189+
test("should handle cross compilation flag", () => {
190+
const result = parseBuiltInConfigs("linux-x64-cross")
191+
expect(result.os).toBe("linux")
192+
expect(result.arch).toBe("x64")
193+
expect(result.cross).toBe(true)
194+
})
195+
196+
test("should throw error for invalid config parts", () => {
197+
expect(() => parseBuiltInConfigs("invalid-os-x64")).toThrow()
198+
})
199+
})
200+
201+
suite("getConfigFile", () => {
202+
test("should return config from package.json", async () => {
203+
const result = await getConfigFile(testPackageJsonPath)
204+
expect(result).toEqual(mockConfigFile)
205+
})
206+
207+
test("should return empty object when package.json is not found", async () => {
208+
const result = await getConfigFile(join(process.cwd(), "non-existent-package.json"))
209+
expect(result).toEqual({})
210+
})
211+
212+
test("should return empty object when cmake-ts key is missing", async () => {
213+
await writeJson(testPackageJsonPath, {
214+
name: "test-package",
215+
version: "1.0.0",
216+
})
217+
const result = await getConfigFile(testPackageJsonPath)
218+
expect(result).toEqual({})
219+
})
220+
})
221+
})

0 commit comments

Comments
 (0)