Generate compiler option definitions, create JSON schema - #64457
Jake Bailey (jakebailey) wants to merge 20 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.
I think this is the only open question in this PR.
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.
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"), |
There was a problem hiding this comment.
This is actually preexisting, we're apparently not processing these at all?
There was a problem hiding this comment.
will someone please just delete watchOptions; I keep fighting off people's Copilots desperately trying to resurface it in the API
There was a problem hiding this comment.
Should we just delete it? We'd then consider it to be random junk at the top level which is legal. Maybe that's for the best?
There was a problem hiding this comment.
I'm just going to delete it now. It actually complicates this PR a lot because watch options are screwy.
Shared configuration schemas need to recognize older TypeScript configs without restoring removed options to the native compiler or API. Keep historical options and enum values in schema-only metadata, with deprecation annotations and their original value types.
Carry main's ES2026 target and library updates into the shared option metadata and regenerate the dependent definitions and schemas.
Wesley Wigham (weswigham)
left a comment
There was a problem hiding this comment.
Q: Why use a JSON document as a source of truth? Generally speaking we edit go code first and everything flows from there - therefore I'd think the go structs should be the source of truth, and any json files/JS APIs/serializers should be generated from there. It's not like we can't markup the go structs with any metadata we need for any codegen steps.
|
(I say this simply because |
|
Most of this is data, not types; the AST is already generated from a JSON file and a TS script, I just used a TS file as it means easy declaration of diags we have to pull from generated JSON etc. If I want to do this in Go it's just going to be the same thing but more annoying. |
|
+1, I'm currently moving user preferences to be generated from JSON because I think that makes more sense than trying to infer the string literal union TS type to int Go type correspondence from the Go code. The AST and compiler options are the same thing; Go's type system is less expressive than TypeScript's, so to get proper TS types out, you either need a higher fidelity source of truth or you need to rely on heuristics in Go to infer out what was intended to be a literal, a union, an enum, etc. |
Watch settings no longer affect the native filesystem watcher, and the config parser never consumed watchOptions. Accepting these flags and advertising them in help and schemas misleadingly implies support. Remove the unused settings and their supporting code while preserving watch mode itself.


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