Skip to content

Commit

Permalink
fix: add support for custom plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
mrlubos committed Nov 8, 2024
1 parent 404690c commit 07800d4
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/fluffy-tomatoes-warn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hey-api/openapi-ts': patch
---

fix: add support for custom plugins
47 changes: 38 additions & 9 deletions packages/openapi-ts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import {
operationParameterFilterFn,
operationParameterNameFn,
} from './openApi/config';
import type { ClientPlugins } from './plugins';
import { defaultPluginConfigs } from './plugins';
import type { PluginNames } from './plugins/types';
import type { DefaultPluginConfigsMap, PluginNames } from './plugins/types';
import type { Client } from './types/client';
import type { ClientConfig, Config, UserConfig } from './types/config';
import { CLIENTS } from './types/config';
Expand Down Expand Up @@ -164,8 +165,10 @@ const getOutput = (userConfig: ClientConfig): Config['output'] => {
};

const getPluginOrder = ({
pluginConfigs,
userPlugins,
}: {
pluginConfigs: DefaultPluginConfigsMap<ClientPlugins>;
userPlugins: ReadonlyArray<PluginNames>;
}): Config['pluginOrder'] => {
const circularReferenceTracker = new Set<PluginNames>();
Expand All @@ -179,12 +182,19 @@ const getPluginOrder = ({
if (!visitedNodes.has(name)) {
circularReferenceTracker.add(name);

for (const dependency of defaultPluginConfigs[name]._dependencies || []) {
const pluginConfig = pluginConfigs[name];

if (!pluginConfig) {
throw new Error(
`🚫 unknown plugin dependency "${name}" - do you need to register a custom plugin with this name?`,
);
}

Check warning on line 191 in packages/openapi-ts/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/index.ts#L188-L191

Added lines #L188 - L191 were not covered by tests

for (const dependency of pluginConfig._dependencies || []) {
dfs(dependency);
}

for (const dependency of defaultPluginConfigs[name]
._optionalDependencies || []) {
for (const dependency of pluginConfig._optionalDependencies || []) {
if (userPlugins.includes(dependency)) {
dfs(dependency);
}
Expand Down Expand Up @@ -218,18 +228,37 @@ const getPlugins = (
return plugin;
}

// @ts-ignore
// @ts-expect-error
userPluginsConfig[plugin.name] = plugin;
return plugin.name;
});
const pluginOrder = getPluginOrder({ userPlugins });

const pluginOrder = getPluginOrder({
pluginConfigs: {
...userPluginsConfig,
...defaultPluginConfigs,
},
userPlugins,
});

const plugins = pluginOrder.reduce(
(result, name) => {
// @ts-ignore
const defaultOptions = defaultPluginConfigs[name];
const userOptions = userPluginsConfig[name];
if (userOptions && defaultOptions) {
const nativePluginOption = Object.keys(userOptions).find((key) =>
key.startsWith('_'),
);
if (nativePluginOption) {
throw new Error(
`🚫 cannot register plugin "${userOptions.name}" - attempting to override a native plugin option "${nativePluginOption}"`,
);
}

Check warning on line 256 in packages/openapi-ts/src/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/openapi-ts/src/index.ts#L253-L256

Added lines #L253 - L256 were not covered by tests
}
// @ts-expect-error
result[name] = {
...defaultPluginConfigs[name],
...userPluginsConfig[name],
...defaultOptions,
...userOptions,
};
return result;
},
Expand Down
3 changes: 2 additions & 1 deletion packages/openapi-ts/src/plugins/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ export interface PluginName<Name extends PluginNames> {
}

interface CommonConfig {
name: PluginNames;
// eslint-disable-next-line @typescript-eslint/ban-types
name: PluginNames | (string & {});
output?: string;
}

Expand Down
86 changes: 85 additions & 1 deletion packages/openapi-ts/test/plugins.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { readFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';

import { createClient } from '../';
import type { PluginConfig } from '../src/plugins/types';
import type { UserConfig } from '../src/types/config';
import { getFilePaths } from './utils';

Expand Down Expand Up @@ -223,4 +224,87 @@ for (const version of versions) {
});
});
});

describe('custom plugin', () => {
it('handles a custom plugin', async () => {
const myPlugin: PluginConfig<{
customOption: boolean;
name: string;
output: string;
}> = {
_dependencies: ['@hey-api/types'],
_handler: vi.fn(),
_handlerLegacy: vi.fn(),
customOption: true,
name: 'my-plugin',
output: 'my-plugin',
};

await createClient({
client: '@hey-api/client-fetch',
experimentalParser: true,
input: path.join(__dirname, 'spec', '3.1.x', 'full.json'),
output: path.join(outputDir, myPlugin.name, 'default'),
// @ts-expect-error
plugins: [myPlugin],
});

expect(myPlugin._handler).toHaveBeenCalled();
expect(myPlugin._handlerLegacy).not.toHaveBeenCalled();
});

it('throws on invalid dependency', async () => {
const myPlugin: PluginConfig<{
name: string;
output: string;
}> = {
// @ts-expect-error
_dependencies: ['@hey-api/oops'],
_handler: vi.fn(),
_handlerLegacy: vi.fn(),
name: 'my-plugin',
output: 'my-plugin',
};

await expect(() =>
createClient({
client: '@hey-api/client-fetch',
experimentalParser: true,
input: path.join(__dirname, 'spec', '3.1.x', 'full.json'),
output: path.join(outputDir, myPlugin.name, 'default'),
// @ts-expect-error
plugins: [myPlugin],
}),
).rejects.toThrowError(/unknown plugin/g);

expect(myPlugin._handler).not.toHaveBeenCalled();
expect(myPlugin._handlerLegacy).not.toHaveBeenCalled();
});

it('throws on native plugin override', async () => {
const myPlugin: PluginConfig<{
name: string;
output: string;
}> = {
_handler: vi.fn(),
_handlerLegacy: vi.fn(),
name: '@hey-api/types',
output: 'my-plugin',
};

await expect(() =>
createClient({
client: '@hey-api/client-fetch',
experimentalParser: true,
input: path.join(__dirname, 'spec', '3.1.x', 'full.json'),
output: path.join(outputDir, myPlugin.name, 'default'),
// @ts-expect-error
plugins: [myPlugin],
}),
).rejects.toThrowError(/cannot register plugin/g);

expect(myPlugin._handler).not.toHaveBeenCalled();
expect(myPlugin._handlerLegacy).not.toHaveBeenCalled();
});
});
}

0 comments on commit 07800d4

Please sign in to comment.