Generate compiler option definitions, create JSON schema - #64457
Jake Bailey (jakebailey) wants to merge 38 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.
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.
|
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.
Welp, if we'd really rather that be the case, then |
That's easy to fix; will do. |
Deriving the API interface from generated Go makes compiler option changes depend on the order of two generators. Use the option metadata as the source for both languages so the protocol generator can import the API types without traversing the generated Go fields. Keep PluginImport in the same metadata to avoid a dependency back on the protocol output, while preserving the existing API shape and exports.
Enum display names should stay aligned with their declarations and configuration spellings. Preserve the existing diagnostics and strict handling of zero and invalid values without another handwritten switch.
Single-file transpilation needs one source for both cleared and forced options. Keep its declaration-mode and verbatim-module exceptions in that source instead of splitting the policy between metadata and code. The transpilation rules no longer need space in runtime declarations.
The generated serializer already excludes most of the options removed by the handwritten post-processing list. Keep the remaining exception with its option metadata so serialization has one filtering step.
File-listing options should not become part of the configuration printed by showConfig. Omitting listFiles alongside listEmittedFiles restores that behavior while keeping explainFiles visible and preserving which options are accepted in tsconfig files.
Keep the canonical schemas at their published package paths so normal package staging includes them without a separate copy step.
New option descriptions can reference diagnostics that have not been generated yet. Running enum verification while generating options then loads an incomplete Go tree and stops generation before diagnostics can catch up. Finish the bulk Go-source generation pass before loading Go packages, while retaining the standalone compiler-options task chaining.
Assertions about generated comments, field lists, and implementation text duplicate metadata or existing behavioral coverage without adding independent assurance. Keep schema validation, artifact freshness, and behavioral checks, and focus transpile tests on conditional overrides rather than repeating every generated assignment. Limit this cleanup to tests introduced by this branch.
| comment?: string; | ||
| parseAliases?: string[]; | ||
| parser?: "lib" | "plugins"; | ||
| declarations?: (DeclarationMetadata & { |
There was a problem hiding this comment.
What compiler option actually has multiple of these since you removed watch options?
There was a problem hiding this comment.
--help, but we can just handle that differently.
Only help needs multiple short names, so an array of full declarations adds unnecessary complexity to every option. Represent its extra alias separately while preserving generated declarations and help output.
…ptions # Conflicts: # packages/typescript/src/api/proto.generated.ts # tools/gen-proto/main.go # tools/gen-proto/main_test.go
Alias expansion omits help text, causing inference to produce a union whose alias member has no help properties. Expose the shared declaration type so consumers can read those optional properties safely.
Protocol generation now ensures user preferences are generated first. The nested compiler-options task therefore reports one additional cache hit when all generated outputs are current.
Direct option generation no longer needs Go API visibility tags or runtime flags retained only for reflection-based comparison tests. Option assignment functions never report diagnostics; validation happens before assignment. Drop the unused return values and their plumbing without changing validation or compiler variation metadata.
Test variation eligibility should follow an option's finite set of values, not unrelated incremental-build metadata. Generate the eligible set from boolean and enum declarations instead of retaining runtime affects flags solely for the test harness. Keep the affects metadata needed for production code generation and check its build-info invariant at generation time. Keep metadata explanations in the source rather than repeating them in generated declarations.
The allowJs comparison needs special handling for its effective value, but a separate flag adds no information beyond the option's name.
Redundant parser selectors and unreachable comparison handling obscure which metadata actually controls generated behavior. Keep special parsing tied to the option names and restrict extra validation to its supported locale selector. Explicitly select runtime declaration fields so new generation-only metadata cannot accidentally become Go field initializers.
Section headings should preserve visual grouping without becoming Go field documentation or TypeScript JSDoc. Represent them separately from field comments and keep them detached in both generated outputs. Remove the unused no-validation constant and the misplaced build-option comment while retaining the internal-field sections.
The protocol types also consume BuildOptions field comments. Regenerate them after detaching section headings so CI generation no longer removes a stale JSDoc comment from the checked-in output.



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