Skip to content

Commit 27ae457

Browse files
committed
[heft-json-schema-typings-plugin] Make $schema stripping configurable via includeSchemaMetadata option
1 parent 55aecaa commit 27ae457

7 files changed

Lines changed: 65 additions & 11 deletions

File tree

common/changes/@rushstack/heft-json-schema-typings-plugin/strip-schema-property_2026-02-18-13-17.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"changes": [
33
{
44
"packageName": "@rushstack/heft-json-schema-typings-plugin",
5-
"comment": "Strip the `$schema` metadata property from JSON schema files before generating TypeScript typings, so it does not appear as a property in the generated `.d.ts` declarations.",
5+
"comment": "Strip the `$schema` metadata property from JSON schema files before generating TypeScript typings, so it does not appear as a property in the generated `.d.ts` declarations. This behavior can be disabled by setting the new `includeSchemaMetadata` option to `true`.",
66
"type": "patch"
77
}
88
],

heft-plugins/heft-json-schema-typings-plugin/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ to produce type declarations that can be imported alongside the schema at build
4848
| `srcFolder` | `string` | `"src"` | Source directory to scan for `*.schema.json` files. |
4949
| `generatedTsFolders` | `string[]` | `["temp/schemas-ts"]`| Output directories for the generated `.d.ts` files. |
5050
| `formatWithPrettier` | `boolean` | `false` | When `true`, format generated typings with [prettier](https://prettier.io/). Requires `prettier` as an installed dependency. |
51+
| `includeSchemaMetadata` | `boolean` | `false` | When `true`, keep the `$schema` property in the generated typings. By default it is stripped so it does not appear as a field in the emitted types. |
5152

5253
## Vendor extension: `x-tsdoc-release-tag`
5354

heft-plugins/heft-json-schema-typings-plugin/src/JsonSchemaTypingsGenerator.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ interface IJsonSchemaTypingsGeneratorBaseOptions extends ITypingsGeneratorBaseOp
2121
* Enabling this requires the `prettier` package to be installed as a dependency.
2222
*/
2323
formatWithPrettier?: boolean;
24+
25+
/**
26+
* If true, include the `$schema` property in the generated typings. Defaults to false.
27+
*
28+
* @remarks
29+
* By default, the `$schema` metadata property is stripped from the JSON schema before
30+
* generating TypeScript declarations so that it does not appear as a field in the
31+
* emitted types. Set this to `true` if you need the `$schema` property to be
32+
* part of the generated interface.
33+
*/
34+
includeSchemaMetadata?: boolean;
2435
}
2536

2637
const SCHEMA_FILE_EXTENSION: '.schema.json' = '.schema.json';
@@ -32,7 +43,7 @@ interface IExtendedJson4Schema extends Json4Schema {
3243

3344
export class JsonSchemaTypingsGenerator extends TypingsGenerator {
3445
public constructor(options: IJsonSchemaTypingsGeneratorBaseOptions) {
35-
const { formatWithPrettier = false, ...otherOptions } = options;
46+
const { formatWithPrettier = false, includeSchemaMetadata = false, ...otherOptions } = options;
3647
super({
3748
...otherOptions,
3849
fileExtensions: [SCHEMA_FILE_EXTENSION],
@@ -43,13 +54,19 @@ export class JsonSchemaTypingsGenerator extends TypingsGenerator {
4354
relativePath: string
4455
): Promise<string> => {
4556
const parsedFileContents: IExtendedJson4Schema = JSON.parse(fileContents);
46-
const {
47-
[X_TSDOC_RELEASE_TAG_KEY]: tsdocReleaseTag,
48-
// Strip $schema so it doesn't appear as a property in the generated types
57+
const { [X_TSDOC_RELEASE_TAG_KEY]: tsdocReleaseTag, ...jsonSchemaWithoutReleaseTag } =
58+
parsedFileContents;
59+
60+
// Unless includeSchemaMetadata is true, strip $schema so it doesn't appear
61+
// as a property in the generated types.
62+
let schemaForCompilation: Json4Schema;
63+
if (includeSchemaMetadata) {
64+
schemaForCompilation = jsonSchemaWithoutReleaseTag;
65+
} else {
4966
// eslint-disable-next-line @typescript-eslint/no-unused-vars
50-
$schema: _schema,
51-
...jsonSchemaWithoutMetadata
52-
} = parsedFileContents;
67+
const { $schema: _schema, ...rest } = jsonSchemaWithoutReleaseTag;
68+
schemaForCompilation = rest;
69+
}
5370

5471
// Use the absolute directory of the schema file so that cross-file $ref
5572
// (e.g. { "$ref": "./other.schema.json" }) resolves correctly.
@@ -58,7 +75,7 @@ export class JsonSchemaTypingsGenerator extends TypingsGenerator {
5875
dirname.length + 1,
5976
-SCHEMA_FILE_EXTENSION.length
6077
);
61-
let typings: string = await compile(jsonSchemaWithoutMetadata, filenameWithoutExtension, {
78+
let typings: string = await compile(schemaForCompilation, filenameWithoutExtension, {
6279
// The typings generator adds its own banner comment
6380
bannerComment: '',
6481
cwd: dirname,

heft-plugins/heft-json-schema-typings-plugin/src/JsonSchemaTypingsPlugin.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface IJsonSchemaTypingsPluginOptions {
1919
srcFolder?: string;
2020
generatedTsFolders?: string[];
2121
formatWithPrettier?: boolean;
22+
includeSchemaMetadata?: boolean;
2223
}
2324

2425
export default class JsonSchemaTypingsPlugin implements IHeftTaskPlugin<IJsonSchemaTypingsPluginOptions> {
@@ -35,7 +36,12 @@ export default class JsonSchemaTypingsPlugin implements IHeftTaskPlugin<IJsonSch
3536
hooks: { run, runIncremental }
3637
} = taskSession;
3738
const { buildFolderPath } = heftConfiguration;
38-
const { srcFolder = 'src', generatedTsFolders = ['temp/schemas-ts'], formatWithPrettier } = options;
39+
const {
40+
srcFolder = 'src',
41+
generatedTsFolders = ['temp/schemas-ts'],
42+
formatWithPrettier,
43+
includeSchemaMetadata
44+
} = options;
3945

4046
const resolvedTsFolders: string[] = [];
4147
for (const generatedTsFolder of generatedTsFolders) {
@@ -49,7 +55,8 @@ export default class JsonSchemaTypingsPlugin implements IHeftTaskPlugin<IJsonSch
4955
generatedTsFolder,
5056
secondaryGeneratedTsFolders,
5157
terminal,
52-
formatWithPrettier
58+
formatWithPrettier,
59+
includeSchemaMetadata
5360
});
5461

5562
run.tapPromise(PLUGIN_NAME, async () => {

heft-plugins/heft-json-schema-typings-plugin/src/schemas/heft-json-schema-typings-plugin.schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
"formatWithPrettier": {
2424
"type": "boolean",
2525
"description": "If true, format generated typings with prettier. Defaults to false."
26+
},
27+
28+
"includeSchemaMetadata": {
29+
"type": "boolean",
30+
"description": "If true, include the $schema property in the generated typings. Defaults to false."
2631
}
2732
}
2833
}

heft-plugins/heft-json-schema-typings-plugin/src/test/JsonSchemaTypingsGenerator.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,16 @@ describe('JsonSchemaTypingsGenerator', () => {
7272
expect(typings).toMatchSnapshot();
7373
expect(typings).not.toContain('$schema');
7474
});
75+
76+
it('succeeds with includeSchemaMetadata enabled', async () => {
77+
const generator = new JsonSchemaTypingsGenerator({
78+
srcFolder: schemasFolder,
79+
generatedTsFolder: outputFolder,
80+
includeSchemaMetadata: true
81+
});
82+
83+
await generator.generateTypingsAsync(['with-schema-field.schema.json']);
84+
const typings: string = await readGeneratedTypings('with-schema-field.schema.json');
85+
expect(typings).toMatchSnapshot();
86+
});
7587
});

heft-plugins/heft-json-schema-typings-plugin/src/test/__snapshots__/JsonSchemaTypingsGenerator.test.ts.snap

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,15 @@ name?: string
9595
}
9696
"
9797
`;
98+
99+
exports[`JsonSchemaTypingsGenerator succeeds with includeSchemaMetadata enabled 1`] = `
100+
"// This file was generated by a tool. Modifying it will produce unexpected behavior
101+
102+
export interface ConfigWithSchemaField {
103+
/**
104+
* The name.
105+
*/
106+
name?: string
107+
}
108+
"
109+
`;

0 commit comments

Comments
 (0)