Skip to content

Commit

Permalink
feat: introduce plugins option (#76)
Browse files Browse the repository at this point in the history
  • Loading branch information
toomuchdesign authored Sep 18, 2023
1 parent 62ded69 commit 79feb32
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 54 deletions.
5 changes: 5 additions & 0 deletions .changeset/pretty-carpets-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openapi-ts-json-schema': minor
---

Introduce plugins option
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type MyModel = FromSchema<typeof myModelSchema>;
| **refHandling** | `"inline" \| "import" \| "keep"` | `"inline"`: inline `$ref` schemas. <br/>`"import"`: generate and import `$ref` schemas.<br/>`"keep"`: keep `$ref` value. | `"inline"` |
| **schemaPatcher** | `(params: { schema: JSONSchema }) => void` | Dynamically patch generated JSON schemas. The provided function will be invoked against every single JSON schema node. | - |
| **outputPath** | `string` | Path where the generated schemas will be saved. Defaults to `/schemas-autogenerated` in same directory as provided `openApiSchema`. | - |
| **plugins** | `ReturnType<Plugin>[]` | A set of optional plugin to generate any extra custom. See [plugins docs](./docs/plugins.md). output. | - |
| **silent** | `boolean` | Don't `console.log` user messages. | `false` |

### Notes
Expand Down
3 changes: 1 addition & 2 deletions docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ const { outputPath, metaData } = await openapiToTsJsonSchema({
outputPath: 'path/to/generated/schemas',
definitionPathsToGenerateFrom: ['components.schemas', 'paths'],
refHandling: 'keep',
plugins: [fastifyTypeProviderPlugin()],
});

await fastifyTypeProviderPlugin({ outputPath, metaData });
```

Setup `Fastify` and `json-schema-to-ts` type provider:
Expand Down
24 changes: 21 additions & 3 deletions src/openapiToTsJsonSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,31 @@ import {
addSchemaToMetaData,
pathToRef,
} from './utils';
import type { SchemaPatcher, SchemaMetaDataMap, JSONSchema } from './types';
import type {
SchemaPatcher,
SchemaMetaDataMap,
JSONSchema,
ReturnPayload,
Plugin,
} from './types';

export async function openapiToTsJsonSchema({
openApiSchema: openApiSchemaRelative,
definitionPathsToGenerateFrom,
schemaPatcher,
outputPath: providedOutputPath,
plugins = [],
silent,
refHandling = 'inline',
}: {
openApiSchema: string;
definitionPathsToGenerateFrom: string[];
schemaPatcher?: SchemaPatcher;
outputPath?: string;
plugins?: ReturnType<Plugin>[];
silent?: boolean;
refHandling?: 'inline' | 'import' | 'keep';
}): Promise<{ outputPath: string; metaData: { schemas: SchemaMetaDataMap } }> {
}): Promise<ReturnPayload> {
if (definitionPathsToGenerateFrom.length === 0 && !silent) {
console.log(
`[openapi-ts-json-schema] ⚠️ No schemas will be generated since definitionPathsToGenerateFrom option is empty`,
Expand Down Expand Up @@ -144,11 +152,21 @@ export async function openapiToTsJsonSchema({
schemaMetaDataMap,
});

const returnPayload: ReturnPayload = {
outputPath,
metaData: { schemas: schemaMetaDataMap },
};

// Process plugins
for (const plugin of plugins) {
await plugin(returnPayload);
}

if (!silent) {
console.log(
`[openapi-ts-json-schema] ✅ JSON schema models generated at ${outputPath}`,
);
}

return { outputPath, metaData: { schemas: schemaMetaDataMap } };
return returnPayload;
}
90 changes: 46 additions & 44 deletions src/plugins/fastifyTypeProviderPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,61 @@ import type { Plugin, SchemaMetaData } from '../types';

const FILE_NAME = 'fastifyTypeProvider.ts';

const fastifyTypeProviderPlugin: Plugin = async ({ outputPath, metaData }) => {
const refSchemaMetaData: SchemaMetaData[] = [];
metaData.schemas.forEach((schema) => {
if (schema.isRef) {
refSchemaMetaData.push(schema);
}
});

const schemas = refSchemaMetaData.map(
({ schemaAbsoluteImportPath, schemaUniqueName, schemaId }) => {
return {
importPath: makeRelativePath({
fromDirectory: outputPath,
to: schemaAbsoluteImportPath,
}),
schemaUniqueName,
schemaId,
};
},
);

let output = '';

schemas.forEach((schema) => {
output += `\n import ${schema.schemaUniqueName} from "${schema.importPath}";`;
});

output += '\n\n';

schemas.forEach((schema) => {
output += `\n const ${schema.schemaUniqueName}WithId = {...${schema.schemaUniqueName}, $id: "${schema.schemaId}"} as const;`;
});

// Generate a TS tuple type containing the types of all $ref schema found
output += `\n\n
const fastifyTypeProviderPlugin: Plugin =
() =>
async ({ outputPath, metaData }) => {
const refSchemaMetaData: SchemaMetaData[] = [];
metaData.schemas.forEach((schema) => {
if (schema.isRef) {
refSchemaMetaData.push(schema);
}
});

const schemas = refSchemaMetaData.map(
({ schemaAbsoluteImportPath, schemaUniqueName, schemaId }) => {
return {
importPath: makeRelativePath({
fromDirectory: outputPath,
to: schemaAbsoluteImportPath,
}),
schemaUniqueName,
schemaId,
};
},
);

let output = '';

schemas.forEach((schema) => {
output += `\n import ${schema.schemaUniqueName} from "${schema.importPath}";`;
});

output += '\n\n';

schemas.forEach((schema) => {
output += `\n const ${schema.schemaUniqueName}WithId = {...${schema.schemaUniqueName}, $id: "${schema.schemaId}"} as const;`;
});

// Generate a TS tuple type containing the types of all $ref schema found
output += `\n\n
export type References = [
${schemas
.map((schema) => `typeof ${schema.schemaUniqueName}WithId`)
.join(',')}
];`;

// Generate an array af all $ref schema
// @TODO make selected schemas configurable
output += `\n\n
// Generate an array af all $ref schema
// @TODO make selected schemas configurable
output += `\n\n
export const referenceSchemas = [
${schemas.map((schema) => `${schema.schemaUniqueName}WithId`).join(',')}
];`;

const formattedOutput = await formatTypeScript(output);
await saveFile({
path: [outputPath, FILE_NAME],
data: formattedOutput,
});
};
const formattedOutput = await formatTypeScript(output);
await saveFile({
path: [outputPath, FILE_NAME],
data: formattedOutput,
});
};

export default fastifyTypeProviderPlugin;
9 changes: 7 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@ export type SchemaMetaData = {
schema: JSONSchema;
isRef: boolean;
};

export type SchemaMetaDataMap = Map<
string, // Schema file relative path
SchemaMetaData
>;

export type Plugin = (args: {
export type ReturnPayload = {
outputPath: string;
metaData: { schemas: SchemaMetaDataMap };
}) => Promise<void>;
};

export type Plugin<Options = void> = (
options: Options,
) => (args: ReturnPayload) => Promise<void>;
5 changes: 2 additions & 3 deletions test/plugins/fastifyTypeProviderPlugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@ const fixtures = path.resolve(__dirname, '../fixtures');

describe('fastifyTypeProviderPlugin plugin', () => {
it('generates expected file', async () => {
const { outputPath, metaData } = await openapiToTsJsonSchema({
const { outputPath } = await openapiToTsJsonSchema({
openApiSchema: path.resolve(fixtures, 'complex/specs.yaml'),
outputPath: path.resolve(
fixtures,
'complex/schemas-autogenerated-fastifyTypeProviderPlugin',
),
definitionPathsToGenerateFrom: ['components.months', 'paths'],
refHandling: 'keep',
plugins: [fastifyTypeProviderPlugin()],
silent: true,
});

await fastifyTypeProviderPlugin({ outputPath, metaData });

const actualAsText = await fs.readFile(
path.resolve(outputPath, 'fastifyTypeProvider.ts'),
{
Expand Down

0 comments on commit 79feb32

Please sign in to comment.