Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
abafa2d
feat: allow an array of configs in openapi-ts.config
carson2222 Sep 4, 2025
bf3af31
chore: replace unconfig to forked updated c12
carson2222 Sep 5, 2025
d3c2666
feat: multiple input support
carson2222 Sep 8, 2025
a49c62b
feat: multiple output support
carson2222 Sep 10, 2025
c1eeb67
chore: update @hey-api/json-schema-ref-parser to version 1.2.0
carson2222 Sep 10, 2025
1450461
chore: replace c12 dependency with @hey-api/c12 version 1.0.3
carson2222 Sep 11, 2025
e877adf
Merge branch 'main' into feat--multiple-configs-functionality
carson2222 Sep 11, 2025
9135044
chore: test formatting update
carson2222 Sep 11, 2025
68e0a10
chore: update tests, docs and address reviews
carson2222 Sep 11, 2025
bf91298
chore: build fix
carson2222 Sep 11, 2025
c84f10e
chore: add changeset
carson2222 Sep 11, 2025
1e0df71
refactor: streamline output path handling and enhance multi-output co…
carson2222 Sep 18, 2025
0ff1680
chore: bump c12 back to unjs original vesrion, update error handling …
carson2222 Sep 19, 2025
383756c
Merge branch 'main' of https://github.com/hey-api/openapi-ts into fea…
carson2222 Sep 22, 2025
86b5ba8
fix: update UserConfigMultiOutputs to use UserOutput type for output …
carson2222 Sep 22, 2025
e1202c8
chore: test update after placement api
carson2222 Sep 22, 2025
c1b2a8c
Merge branch 'main' into feat--multiple-configs-functionality
mrlubos Sep 22, 2025
597a298
Merge branch 'main' of https://github.com/hey-api/openapi-ts into fea…
mrlubos Sep 22, 2025
4ebe34c
Merge branch 'main' into feat--multiple-configs-functionality
mrlubos Sep 23, 2025
71588cb
refactor: clean up some types and diff
mrlubos Sep 23, 2025
e34fbf9
Merge branch 'main' into feat--multiple-configs-functionality
mrlubos Sep 23, 2025
b296d50
Merge branch 'main' of https://github.com/hey-api/openapi-ts into fea…
mrlubos Oct 2, 2025
41900bd
Merge branch 'main' into feat--multiple-configs-functionality
mrlubos Oct 3, 2025
4bb0b94
feat: clean up multi-config matrix
mrlubos Oct 4, 2025
d22a26e
chore: upgrade turborepo
mrlubos Oct 5, 2025
eb7a6fd
test: update types
mrlubos Oct 5, 2025
d20b54f
chore: improve logs
mrlubos Oct 5, 2025
e35dc0b
test: update tests
mrlubos Oct 6, 2025
458441d
docs: add advanced section to configuration
mrlubos Oct 6, 2025
c71e0e8
chore: remove unused specs
mrlubos Oct 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/nuxt/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,17 @@ export default defineNuxtModule<ModuleOptions>({
config.watch = false;
}

const outputPath = Array.isArray(config.output)
? typeof config.output[0] === 'string'
? config.output[0]
: config.output[0]?.path
: typeof config.output === 'string'
? config.output
: config.output.path;

const folder = path.resolve(
nuxt.options.rootDir,
typeof config.output === 'string' ? config.output : config.output.path,
outputPath || path.join(nuxt.options.buildDir, 'client'),
);

nuxt.options.alias[options.alias!] = folder;
Expand Down
216 changes: 213 additions & 3 deletions packages/openapi-ts-tests/main/test/2.0.x.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ describe(`OpenAPI ${version}`, () => {
version,
typeof userConfig.input === 'string'
? userConfig.input
: (userConfig.input.path as string),
: Array.isArray(userConfig.input)
? (userConfig.input[0] as any).path || userConfig.input[0]
: (userConfig.input as any).path,
);
return {
plugins: ['@hey-api/typescript'],
Expand All @@ -38,7 +40,13 @@ describe(`OpenAPI ${version}`, () => {
},
output: path.join(
outputDir,
typeof userConfig.output === 'string' ? userConfig.output : '',
typeof userConfig.output === 'string'
? userConfig.output
: Array.isArray(userConfig.output)
? typeof userConfig.output[0] === 'string'
? userConfig.output[0]
: (userConfig.output[0] as any).path || ''
: (userConfig.output as any).path || '',
),
};
};
Expand Down Expand Up @@ -392,7 +400,13 @@ describe(`OpenAPI ${version}`, () => {
await createClient(config);

const outputPath =
typeof config.output === 'string' ? config.output : config.output.path;
typeof config.output === 'string'
? config.output
: Array.isArray(config.output)
? typeof config.output[0] === 'string'
? config.output[0]
: (config.output[0] as any).path
: (config.output as any).path;
const filePaths = getFilePaths(outputPath);

await Promise.all(
Expand All @@ -409,4 +423,200 @@ describe(`OpenAPI ${version}`, () => {
}),
);
});

describe('multi config', () => {
it('generates outputs for all configs', async () => {
const configA = createConfig({
input: 'external.yaml',
output: 'multi-external',
});
const configB = createConfig({
input: 'enum-names-values.json',
output: 'multi-enum-names-values',
});

await createClient([configA, configB]);

const outputPathA =
typeof configA.output === 'string'
? configA.output
: Array.isArray(configA.output)
? typeof configA.output[0] === 'string'
? configA.output[0]
: (configA.output[0] as any).path
: (configA.output as any).path;
const outputPathB =
typeof configB.output === 'string'
? configB.output
: Array.isArray(configB.output)
? typeof configB.output[0] === 'string'
? configB.output[0]
: (configB.output[0] as any).path
: (configB.output as any).path;

const filesA = getFilePaths(outputPathA);
const filesB = getFilePaths(outputPathB);

await Promise.all(
filesA.map(async (filePath) => {
const fileContent = fs.readFileSync(filePath, 'utf-8');
await expect(fileContent).toMatchFileSnapshot(
path.join(
__dirname,
'__snapshots__',
version,
filePath.slice(outputDir.length + 1),
),
);
}),
);

await Promise.all(
filesB.map(async (filePath) => {
const fileContent = fs.readFileSync(filePath, 'utf-8');
await expect(fileContent).toMatchFileSnapshot(
path.join(
__dirname,
'__snapshots__',
version,
filePath.slice(outputDir.length + 1),
),
);
}),
);
});
});

describe('multi input', () => {
it('parses multiple inputs (object + string) without errors', async () => {
const specsBase = path.join(getSpecsPath(), version);
await expect(
createClient({
dryRun: true,
input: [
{ path: path.join(specsBase, 'multi-a.json') },
path.join(specsBase, 'multi-b.json'),
],
logs: { level: 'silent' },
output: path.join(outputDir, 'multi-input'),
plugins: ['@hey-api/typescript'],
}),
).resolves.not.toThrow();
});
});

describe('multi output', () => {
it('generates multiple string outputs without errors', async () => {
const results = await createClient({
input: path.join(getSpecsPath(), version, 'external.yaml'),
logs: { level: 'silent' },
output: [
path.join(outputDir, 'multi-output-string-1'),
path.join(outputDir, 'multi-output-string-2'),
],
plugins: ['@hey-api/typescript'],
});

expect(results).toHaveLength(2);

// Verify both output directories were created
expect(fs.existsSync(path.join(outputDir, 'multi-output-string-1'))).toBe(
true,
);
expect(fs.existsSync(path.join(outputDir, 'multi-output-string-2'))).toBe(
true,
);
});

it('generates multiple output objects with different configurations', async () => {
const results = await createClient({
input: path.join(getSpecsPath(), version, 'external.yaml'),
logs: { level: 'silent' },
output: [
{
clean: true,
indexFile: true,
path: path.join(outputDir, 'multi-output-config-1'),
},
{
clean: false,
indexFile: false,
path: path.join(outputDir, 'multi-output-config-2'),
},
],
plugins: ['@hey-api/typescript'],
});

expect(results).toHaveLength(2);

// Verify both output directories were created
expect(fs.existsSync(path.join(outputDir, 'multi-output-config-1'))).toBe(
true,
);
expect(fs.existsSync(path.join(outputDir, 'multi-output-config-2'))).toBe(
true,
);

// Verify index files are created/not created based on configuration
expect(
fs.existsSync(
path.join(outputDir, 'multi-output-config-1', 'index.ts'),
),
).toBe(true);
expect(
fs.existsSync(
path.join(outputDir, 'multi-output-config-2', 'index.ts'),
),
).toBe(false);
});

it('generates mixed string and object outputs', async () => {
const results = await createClient({
input: path.join(getSpecsPath(), version, 'external.yaml'),
logs: { level: 'silent' },
output: [
path.join(outputDir, 'multi-output-mixed-string'),
{
indexFile: false,
path: path.join(outputDir, 'multi-output-mixed-object'),
},
],
plugins: ['@hey-api/typescript'],
});

expect(results).toHaveLength(2);

// Verify both output directories were created
expect(
fs.existsSync(path.join(outputDir, 'multi-output-mixed-string')),
).toBe(true);
expect(
fs.existsSync(path.join(outputDir, 'multi-output-mixed-object')),
).toBe(true);
});

it('preserves global configuration across multiple outputs', async () => {
const results = await createClient({
experimentalParser: true,
input: path.join(getSpecsPath(), version, 'external.yaml'),
logs: { level: 'silent' },
output: [
path.join(outputDir, 'multi-output-global-1'),
path.join(outputDir, 'multi-output-global-2'),
],
plugins: ['@hey-api/typescript', '@hey-api/sdk'],
});

expect(results).toHaveLength(2);

// Both results should have the same global configuration
results.forEach((result) => {
if ('config' in result) {
expect(result.config.experimentalParser).toBe(true);
expect(result.config.plugins['@hey-api/typescript']).toBeDefined();
expect(result.config.plugins['@hey-api/sdk']).toBeDefined();
}
});
});
});
});
Loading
Loading