Generate compiler option definitions, create JSON schema - #64457
Jake Bailey (jakebailey) wants to merge 16 commits into
Conversation
Compiler option metadata is duplicated across compiler declarations, API types, enum mappings, and configuration schemas. Maintaining these surfaces independently makes new options and compatibility changes easy to miss. Use one authoritative definition so these surfaces stay synchronized, while preserving compiler behavior and providing reusable config schemas.
Single-file transpilation must ignore options that only make sense for whole-program builds. Keeping a separate clearing list lets it drift from the existing transpile metadata whenever options are added. Derive the clearing list from that metadata while leaving mode-specific overrides and conditional behavior in the transpile worker.
Option enums and SyntaxKind already have authoritative metadata. Reading generated Go back into the TypeScript generator adds an unnecessary intermediate representation and points contributors at the wrong source. Use those definitions directly while retaining one shared enum emitter and Go-value verification for every input source.
Ship version-matched configuration schemas with the main package so tools can use the installed compiler version without relying on a hosted schema.
| "./schemas/tsconfig.schema.json": "./schemas/tsconfig.schema.json", | ||
| "./schemas/jsconfig.schema.json": "./schemas/jsconfig.schema.json", |
There was a problem hiding this comment.
I generated these into tsc/internal/tsoptions/schemas. Perhaps I should have just checked them in here?
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The schemas incorrectly deprecate active compiler options and lose draft-07 $ref sibling documentation.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (3)
What changed in this PR
Centralizes compiler-option metadata to generate Go/TypeScript definitions and publish version-matched configuration schemas.
Changes:
- Generates option structs, parsers, enums, maps, defaults, and transpile handling.
- Adds generated
tsconfig/jsconfigschemas to the npm package. - Expands code-generation, schema, packaging, and compatibility tests.
| File | Description |
|---|---|
Herebyfile.mjs |
Integrates generation, validation, and schema packaging. |
packages/typescript/package.json |
Exports packaged schemas. |
packages/typescript/src/enums/jsxEmit.enum.ts |
Updates generation source. |
packages/typescript/src/enums/jsxEmit.ts |
Updates generation source. |
packages/typescript/src/enums/moduleDetectionKind.enum.ts |
Updates generation source. |
packages/typescript/src/enums/moduleDetectionKind.ts |
Updates generation source. |
packages/typescript/src/enums/moduleKind.enum.ts |
Updates generation source. |
packages/typescript/src/enums/moduleKind.ts |
Updates generation source. |
packages/typescript/src/enums/moduleResolutionKind.enum.ts |
Updates generation source. |
packages/typescript/src/enums/moduleResolutionKind.ts |
Updates generation source. |
packages/typescript/src/enums/newLineKind.enum.ts |
Updates generation source. |
packages/typescript/src/enums/newLineKind.ts |
Updates generation source. |
packages/typescript/src/enums/scriptTarget.enum.ts |
Makes ScriptTarget metadata-generated. |
packages/typescript/src/enums/scriptTarget.ts |
Makes ScriptTarget metadata-generated. |
packages/typescript/src/enums/syntaxKind.enum.ts |
Generates from AST metadata. |
packages/typescript/src/enums/syntaxKind.ts |
Generates from AST metadata. |
tools/scripts/gen/generatedFile.test.mts |
Extends code-generation tests. |
tools/scripts/tsc/generate-enums.ts |
Generates enums from shared metadata. |
tools/scripts/tsc/generate-options.ts |
Adds the option artifact generator. |
tools/scripts/tsc/options-model.ts |
Defines shared metadata types. |
tools/scripts/tsc/options-schema.ts |
Generates configuration schemas. |
tools/scripts/tsc/options.test.ts |
Tests metadata and generated artifacts. |
tools/scripts/tsc/options.ts |
Centralizes option metadata. |
tsc/internal/api/enum_values_generated.go |
Adds generated option enum values. |
tsc/internal/core/buildoptions_generated.go |
Marks build options as generated. |
tsc/internal/core/compileroptions.go |
Removes moved generated definitions. |
tsc/internal/core/compileroptions_generated.go |
Generates CompilerOptions and cloning. |
tsc/internal/core/optionenums_generated.go |
Generates option enums. |
tsc/internal/core/typeacquisition.go |
Removes moved struct definition. |
tsc/internal/core/typeacquisition_generated.go |
Generates TypeAcquisition. |
tsc/internal/core/watchoptions.go |
Removes moved option definitions. |
tsc/internal/core/watchoptions_generated.go |
Generates WatchOptions. |
tsc/internal/transpile/compileroptions_generated.go |
Generates transpile option clearing. |
tsc/internal/transpile/options_test.go |
Tests transpile option isolation. |
tsc/internal/transpile/transpile.go |
Uses generated option clearing. |
tsc/internal/tsoptions/commandlineoption.go |
Removes generated maps. |
tsc/internal/tsoptions/compileroptions_generated.go |
Generates parsing and defaults. |
tsc/internal/tsoptions/compileroptions_test.go |
Tests parsing and cloning. |
tsc/internal/tsoptions/declarations_generated.go |
Generates option declarations. |
tsc/internal/tsoptions/declsbuild.go |
Removes superseded declarations. |
tsc/internal/tsoptions/declscompiler.go |
Retains declaration consumers. |
tsc/internal/tsoptions/declstypeacquisition.go |
Removes superseded declarations. |
tsc/internal/tsoptions/declswatch.go |
Removes superseded declarations. |
tsc/internal/tsoptions/enummaps.go |
Removes generated enum maps. |
tsc/internal/tsoptions/enummaps_generated.go |
Generates enum and library maps. |
tsc/internal/tsoptions/otheroptions_generated.go |
Generates auxiliary parsers. |
tsc/internal/tsoptions/parsinghelpers.go |
Removes generated parsers. |
tsc/internal/tsoptions/rootoptions_generated.go |
Generates root config declarations. |
tsc/internal/tsoptions/schemas/jsconfig.schema.json |
Adds generated jsconfig schema. |
tsc/internal/tsoptions/schemas/tsconfig.schema.json |
Adds generated tsconfig schema. |
tsc/internal/tsoptions/tsconfigparsing.go |
Removes generated declarations/defaults. |
Files not reviewed (4)
- tsc/internal/api/enum_values_generated.go: Generated file
- tsc/internal/core/compileroptions_generated.go: Generated file
- tsc/internal/core/optionenums_generated.go: Generated file
- tsc/internal/core/typeacquisition_generated.go: Generated file
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
| if (option.deprecated) { | ||
| schema.deprecated = true; | ||
| schema.deprecationMessage = "This compiler option is deprecated."; | ||
| } |
There was a problem hiding this comment.
Thank you, knowledge cutoff
There was a problem hiding this comment.
That being said, perhaps I should restore es3/es5 just for the schema, or other options...
There was a problem hiding this comment.
And then going forward we only ever add to the schema for back compat?
Use diagnostic message keys for completion and typo checking instead of untyped Go identifier strings. Preserve the original text for schema output without a separate diagnostic lookup or generation step.
Draft-07 ignores siblings of a reference. Keep root option descriptions outside the reference so consumers retain their documentation and links.
Keep the distinction between build options and compiler options visible in the generated struct, as it was before generation.
Option metadata already identifies which fields affect diagnostics, emit, declaration paths, and build info. Use it to avoid runtime reflection without maintaining another handwritten list of fields. Preserve effective strict defaults and build-info ordering and zero-value semantics, with the previous reflection logic retained as a test oracle.
The option metadata already describes every field and its JSON name. Use it to avoid reflective field access and tag parsing during config merging while keeping new options covered automatically. Preserve explicit-null overrides, nonzero source precedence, and shallow sharing of slices and pointers.
Keep config directory substitution aligned with option metadata instead of maintaining a separate handwritten field list. Explicitly exclude project and pprofDir, preserving the existing substitution behavior and copy-on-write semantics. Keep substitution eligibility out of runtime declarations, narrow the prefix helper to strings, and note the existing case-sensitivity mismatch between prefix detection and replacement.
Reduce handwritten serialization machinery by deriving field handling and enum names from the existing option metadata. This keeps option coverage centralized and makes field access statically checked rather than relying on reflection and runtime assertions. Preserve output ordering, enum aliases, and unset-value behavior while retaining handwritten formatting helpers and implied-option rules.
Keep whole-options equality aligned with the option definitions without runtime reflection. Preserve stored values, pointer contents, collection ordering, and nil-versus-empty distinctions. Compare paths by their ordered entries rather than ordered-map backing storage, so allocation history does not cause spurious project changes.
Aggregate reflection bypassed compiler option equality, making watch mode sensitive to ordered-map backing storage that the project system ignores. Compose option equality methods so both callers honor the same semantics. Treat absent and empty type acquisition lists alike, matching their existing acquisition behavior, while retaining other presence and order distinctions.
Handwritten equality can silently overlook fields added to parsed options or content mappers. Require every field to have a populated test value whose removal changes equality, including promoted mapper fields.
Per-feature outputs fragmented related option code across too many files. Group generated code by package, retaining a separate declarations file in tsoptions, and keep compiler option tests together. This reduces file proliferation without changing generated declarations or test coverage.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The published schema advertises watchOptions, but tsconfig parsing does not recognize or populate them.
Review effort: Balanced
Findings: 2
Open (2)
Resolved since last review (2)
Files not reviewed (1)
- tsc/internal/api/enum_values_generated.go: Generated file
| }; | ||
| const properties: Record<string, JSONSchema> = { | ||
| $schema: { type: "string", description: "The JSON schema used to validate this configuration." }, | ||
| watchOptions: withDescription({ allOf: [{ $ref: "#/definitions/watchOptions" }] }, "Options for watching files and directories.", "watchOptions"), |


For the new website playground, I need some sort of spec for our
CompilerOptions. In Strada, this was done by parsing out at runtime our internal options declarations and turning that into a website UI and JSON schema (if we remembered to do it), which then would eventually get sent over to schemastore (if we remembered to do it...), and then eventually make it into VS Code etc (if we remembered to do it........).Instead, let's just define our options as metadata like we do the AST and other stuff, then code generate all of the Go code, TS code, and even our own JSON schema files. #54192 is a long-open issue requesting that we ship a JSON schema with the package, so, this PR also adds that file to the package too so
node_modules/typescript/schemas/tsconfig.schema.jsonis now valid. Eventually, we can use this in our VSIX, I think, though historically that one's been more permissive of dead options.A good bonus is that we can now codegen
CompilerOptions.Clone,parseCompilerOptions, the transpile options clearing func, and more.Fixes #54192