Skip to content

Commit 1e957f4

Browse files
committed
feat: add cli option, thread through to import-builder
1 parent aa3636e commit 1e957f4

File tree

20 files changed

+191
-50
lines changed

20 files changed

+191
-50
lines changed

packages/documentation/src/lib/playground.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const defaultConfig = {
5757
tsCompilerOptions: {exactOptionalPropertyTypes: false},
5858
enableTypedBasePaths: true,
5959
filenameConvention: "kebab-case",
60+
tsIsEsmProject: false,
6061
tsServerImplementationMethod: "type",
6162
enumExtensibility: "",
6263
} satisfies Config

packages/documentation/src/lib/playground/config-form.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const schema = configSchema.pick({
1717
groupingStrategy: true,
1818
tsAllowAny: true,
1919
tsServerImplementationMethod: true,
20+
tsIsEsmProject: true,
2021
enumExtensibility: true,
2122
})
2223

@@ -37,6 +38,7 @@ export const ConfigForm: React.FC<{
3738
groupingStrategy: config.groupingStrategy,
3839
tsAllowAny: config.tsAllowAny,
3940
tsServerImplementationMethod: config.tsServerImplementationMethod,
41+
tsIsEsmProject: config.tsIsEsmProject,
4042
enumExtensibility: config.enumExtensibility,
4143
} as const,
4244
})
@@ -83,6 +85,11 @@ export const ConfigForm: React.FC<{
8385
name={"tsAllowAny"}
8486
control={control}
8587
/>
88+
<ControlledCheckbox
89+
label={"--ts-is-esm-project"}
90+
name={"tsIsEsmProject"}
91+
control={control}
92+
/>
8693
<ControlledSelect
8794
name={"tsServerImplementationMethod"}
8895
control={control}

packages/openapi-code-generator/src/cli.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {z} from "zod/v4"
1717
import {promptContinue} from "./core/cli-utils"
1818
import {NodeFsAdaptor} from "./core/file-system/node-fs-adaptor"
1919
import type {OperationGroupStrategy} from "./core/input"
20+
import {loadPackageJson} from "./core/loaders/package.json.loader"
2021
import {loadTsConfigCompilerOptions} from "./core/loaders/tsconfig.loader"
2122
import {
2223
loadTypescriptFormatterConfig,
@@ -30,7 +31,7 @@ import {configSchema, generate} from "./index"
3031
import {templateNames} from "./templates"
3132
import type {ServerImplementationMethod} from "./templates.types"
3233

33-
export const boolParser = (arg: string): boolean => {
34+
const optionalBoolParser = (arg: string): boolean | undefined => {
3435
const TRUTHY_VALUES = ["true", "1", "on"]
3536
const FALSY_VALUES = ["false", "0", "off", ""]
3637

@@ -41,6 +42,10 @@ export const boolParser = (arg: string): boolean => {
4142
return false
4243
}
4344

45+
if (!arg) {
46+
return undefined
47+
}
48+
4449
throw new InvalidArgumentError(
4550
`'${arg}' is not a valid boolean parameter. Valid truthy values are: ${TRUTHY_VALUES.map(
4651
(it) => JSON.stringify(it),
@@ -50,6 +55,16 @@ export const boolParser = (arg: string): boolean => {
5055
)
5156
}
5257

58+
export const boolParser = (arg: string): boolean => {
59+
const result = optionalBoolParser(arg)
60+
61+
if (result === undefined) {
62+
throw new InvalidArgumentError(`'${arg}' is not a valid boolean parameter.`)
63+
}
64+
65+
return result
66+
}
67+
5368
export const remoteSpecRequestHeadersParser = (arg: string) => {
5469
return z
5570
.preprocess(
@@ -123,6 +138,14 @@ const program = new Command()
123138
.argParser(boolParser)
124139
.default(false),
125140
)
141+
.addOption(
142+
new Option(
143+
"--ts-is-esm-project [bool]",
144+
`(typescript) whether the target project uss esm or commonjs. auto-detected from package.json when omitted.`,
145+
)
146+
.env("OPENAPI_TS_IS_ESM_PROJECT")
147+
.argParser(optionalBoolParser),
148+
)
126149
.addOption(
127150
new Option(
128151
"--ts-server-implementation-method <value>",
@@ -285,19 +308,25 @@ async function main() {
285308
)
286309

287310
const outputPath = path.join(process.cwd(), config.output)
311+
288312
const formatterOptions = await loadTypescriptFormatterConfig(
289313
outputPath,
290314
fsAdaptor,
291315
)
316+
292317
const formatter = await formatterFactory(formatterOptions)
293318

319+
const projectPackageJson = await loadPackageJson(outputPath, fsAdaptor)
320+
294321
const compilerOptions = await loadTsConfigCompilerOptions(
295322
outputPath,
296323
fsAdaptor,
297324
)
298325

299326
await generate(
300327
configSchema.parse({
328+
// can be overridden by config spread
329+
tsIsEsmProject: projectPackageJson.type === "module",
301330
...config,
302331
tsCompilerOptions: compilerOptions,
303332
}),

packages/openapi-code-generator/src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type Config = {
2727
enumExtensibility: "" | "open" | "closed"
2828
tsAllowAny: boolean
2929
tsServerImplementationMethod: ServerImplementationMethod
30+
tsIsEsmProject: boolean
3031
tsCompilerOptions: CompilerOptions
3132
remoteSpecRequestHeaders?: GenericLoaderRequestHeaders | undefined
3233
}
@@ -64,6 +65,7 @@ export const configSchema = z.object({
6465
enumExtensibility: z.enum(["", "open", "closed"]),
6566
tsAllowAny: z.boolean(),
6667
tsServerImplementationMethod: tsServerImplementationSchema,
68+
tsIsEsmProject: z.boolean(),
6769
tsCompilerOptions: tsconfigSchema.shape.compilerOptions,
6870
remoteSpecRequestHeaders: z
6971
.record(

packages/openapi-code-generator/src/core/file-system/node-fs-adaptor.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,15 @@ export class NodeFsAdaptor implements IFsAdaptor {
1313
}
1414

1515
async exists(path: string) {
16-
const stat = await fs.stat(path)
17-
return stat.isFile()
16+
try {
17+
const stat = await fs.stat(path)
18+
return stat.isFile()
19+
} catch (err) {
20+
if (err instanceof Error && Reflect.get(err, "code") === "ENOENT") {
21+
return false
22+
}
23+
throw err
24+
}
1825
}
1926

2027
existsSync(path: string) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import json5 from "json5"
2+
import {z} from "zod/v4"
3+
import type {IFsAdaptor} from "../file-system/fs-adaptor"
4+
import {loadFileUp} from "./utils"
5+
6+
const schema = z.object({
7+
type: z.enum(["module", "commonjs"]).optional().default("commonjs"),
8+
})
9+
10+
export async function loadPackageJson(
11+
outputPath: string,
12+
fsAdaptor: IFsAdaptor,
13+
) {
14+
const packageJson = json5.parse(
15+
(await loadFileUp("package.json", outputPath, fsAdaptor)) ?? "{}",
16+
)
17+
18+
return schema.parse(packageJson)
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
import path from "node:path"
2+
import type {IFsAdaptor} from "../file-system/fs-adaptor"
3+
14
export function isRemote(location: string): boolean {
25
return location.startsWith("http://") || location.startsWith("https://")
36
}
7+
8+
export async function loadFileUp(
9+
filename: string,
10+
directory: string,
11+
fsAdaptor: IFsAdaptor,
12+
): Promise<string | undefined> {
13+
while (directory !== "/") {
14+
const filePath = path.join(directory, filename)
15+
16+
if (await fsAdaptor.exists(filePath)) {
17+
return fsAdaptor.readFile(filePath)
18+
}
19+
20+
// biome-ignore lint/style/noParameterAssign: walk
21+
directory = path.dirname(directory)
22+
}
23+
24+
return undefined
25+
}

packages/openapi-code-generator/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export async function generate(
8585
compilerOptions: config.tsCompilerOptions,
8686
groupingStrategy: config.groupingStrategy,
8787
filenameConvention: config.filenameConvention,
88+
isEsmProject: config.tsIsEsmProject,
8889
allowAny: config.tsAllowAny,
8990
serverImplementationMethod: config.tsServerImplementationMethod,
9091
})

packages/openapi-code-generator/src/templates.types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export interface OpenapiTypescriptGeneratorConfig
2727
* Which runtime schema parsing library to use
2828
*/
2929
schemaBuilder: SchemaBuilderType
30+
/**
31+
* Whether the project is using ESM, or CommonJS
32+
*/
33+
isEsmProject: boolean
3034
/**
3135
* Sub-set of typescript compiler options relevant to codegen
3236
*/

packages/openapi-code-generator/src/typescript/client/client-servers-builder.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ async function runTest(
1919
"unit-test.ts",
2020
"UnitTest",
2121
servers,
22-
new ImportBuilder(),
22+
new ImportBuilder({includeFileExtensions: false}),
2323
)
2424

2525
for (const it of operations) {

0 commit comments

Comments
 (0)