UNPKG

ttsc

Version:

General-purpose TypeScript-Go compiler, runtime, plugin host, and LSP host.

601 lines 23.2 kB
"use strict"; // SOURCE-OF-TRUTH FLAG SCHEMA for the ttsc / ttsx command-line surface. // // One declaration per flag, consumed by every layer that needs to know about // the flag: // // * `packages/ttsc/src/flags/parser.ts` — runtime TS parsing engine used // by `runTtsc.ts` and `runTtsx.ts`. // * `packages/ttsc/scripts/gen-flags.cjs` — codegen that emits Go // allow-lists and the docs table. // * `packages/ttsc/cmd/ttsc/flags_gen.go` — generated Go allow-list shared // by `cmd/ttsc/*.go` and // `utility/host.go`. // * `packages/lint/linthost/flags_gen.go` — generated Go allow-list shared // by lint subcommand parsers. // * `website/src/content/docs/ttsc/flags.mdx` — generated reference table. // // The generator runs from `pnpm format`; its committed output is the spec the // Go side reads, just like the `gen_shims:hand-maintained` pattern from // AGENTS.md §2.1. Editing the generated files by hand is rejected by the // `format` check. Object.defineProperty(exports, "__esModule", { value: true }); exports.FLAG_BY_TOKEN = exports.FLAG_SCHEMA = void 0; exports.normalizeFlagToken = normalizeFlagToken; exports.resolveFlagSpec = resolveFlagSpec; exports.flagsForSubcommand = flagsForSubcommand; exports.buildGoAllowList = buildGoAllowList; /** * Single source of truth for every flag the ttsc / ttsx CLI accepts. New flags * are added here and only here; the generator rebuilds the parsers and the Go * allow-lists from this table. * * Constraints enforced by the parser and the generator: * * 1. Every flag is uniquely identified by `name`; aliases must not collide with * other flags' `name` or `aliases`. * 2. A flag listed in `consumedBy: ["launcher"]` without a `forwardTo` * consumes-not-forwards. The generator flags this in the docs and the Go * allow-list so the boundary is explicit. * 3. A flag with `subcommands` covering `clean` or `prepare` is parsed by the * project-args lane; the parsing engine accepts the same flag in build / * check / fix / format without a separate parser. */ exports.FLAG_SCHEMA = [ // ------------------------------------------------------------------------- // ttsc — terminal flags (print and exit; never wrapped in pre-emit pass) // ------------------------------------------------------------------------- { name: "--help", aliases: ["-h"], kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["launcher"], terminal: true, description: "Show command help and exit.", }, { name: "--version", aliases: ["-v"], kind: "boolean", subcommands: ["ttsc", "ttsx"], consumedBy: ["launcher"], terminal: true, description: "Print the launcher version and exit.", }, // ------------------------------------------------------------------------- // Project location: shared by every subcommand. // ------------------------------------------------------------------------- { name: "--tsconfig", aliases: ["-p", "--project"], kind: "value", subcommands: [ "ttsc", "ttsx", "build", "cache", "check", "fix", "format", "prepare", "clean", ], consumedBy: ["launcher", "host", "lint"], description: "Resolve project settings from this tsconfig.", }, { name: "--cwd", kind: "value", subcommands: [ "ttsc", "ttsx", "build", "cache", "check", "fix", "format", "prepare", "clean", ], consumedBy: ["launcher", "host", "lint"], description: "Resolve project-relative paths from this directory.", }, // ------------------------------------------------------------------------- // Emit / build mode. // ------------------------------------------------------------------------- { name: "--emit", kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["launcher", "runBuild", "host", "lint"], description: "Force emitted files during build.", }, { name: "--noEmit", kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["launcher", "runBuild", "host", "lint"], internalShadow: true, description: "Force analysis-only build with no file writes.", }, { name: "--outDir", kind: "value", subcommands: ["ttsc", "build", "check"], consumedBy: ["launcher", "host", "lint"], description: "Override compilerOptions.outDir for this invocation.", }, { name: "--composite", kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", tsconfigOnly: true, description: "Configure project-reference constraints in tsconfig; CLI accepts only false or null.", }, { name: "--incremental", aliases: ["-i"], kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Write build information for incremental compilation.", }, { name: "--tsBuildInfoFile", kind: "value", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Choose the incremental build-information file.", }, { name: "--declaration", aliases: ["-d"], kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Emit declaration files.", }, { name: "--declarationDir", kind: "value", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Choose the declaration output directory.", }, { name: "--declarationMap", kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Emit source maps for declaration files.", }, { name: "--emitDeclarationOnly", kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Emit declarations without JavaScript.", }, { name: "--inlineSourceMap", kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Inline source maps into emitted JavaScript.", }, { name: "--sourceMap", kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Emit external JavaScript source maps.", }, { name: "--outFile", kind: "value", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Forward the removed legacy option for TypeScript-Go's diagnostic.", }, { name: "--rootDir", kind: "value", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Choose the compiler input root.", }, { name: "--jsx", kind: "value", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", description: "Choose the JSX emit transform.", }, // ------------------------------------------------------------------------- // Watch mode (launcher only). // ------------------------------------------------------------------------- { name: "--watch", aliases: ["-w"], kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["launcher"], description: "Rebuild when project files change.", }, { name: "--preserveWatchOutput", kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["launcher"], description: "Do not clear the screen between watch rebuilds.", }, // ------------------------------------------------------------------------- // Output verbosity. // ------------------------------------------------------------------------- { name: "--quiet", kind: "boolean", subcommands: ["ttsc", "build", "check", "fix", "format"], consumedBy: ["launcher", "host", "lint"], description: "Keep build output quiet (default).", }, { name: "--verbose", kind: "boolean", subcommands: ["ttsc", "build", "check", "fix", "format"], consumedBy: ["launcher", "host", "lint"], description: "Print the build summary and emitted files.", }, // ------------------------------------------------------------------------- // tsgo-binary / cache plumbing (consumed by launcher, not forwarded). // ------------------------------------------------------------------------- { name: "--binary", kind: "value", subcommands: [ "ttsc", "ttsx", "build", "check", "fix", "format", "prepare", "clean", ], consumedBy: ["launcher"], description: "Use an explicit tsgo binary.", }, { name: "--cache-dir", kind: "value", subcommands: [ "ttsc", "ttsx", "build", "cache", "check", "fix", "format", "prepare", "clean", ], consumedBy: ["launcher"], description: "Override the runner and source-plugin cache root.", }, { name: "--json", kind: "boolean", subcommands: ["cache"], consumedBy: ["launcher"], description: "Print cache paths as JSON.", }, // ------------------------------------------------------------------------- // Threading (tsgo-native; lint host opts in via capability). // ------------------------------------------------------------------------- { name: "--singleThreaded", kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["launcher", "runBuild", "tsgo", "host", "lint"], nativeCapability: "threadingArgs", description: "Run TypeScript-Go single-threaded (one checker).", }, { name: "--checkers", kind: "value", validator: "positiveInt", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["launcher", "runBuild", "tsgo", "host", "lint"], nativeCapability: "threadingArgs", description: "Type-checker pool size (default: TypeScript-Go's).", }, // ------------------------------------------------------------------------- // ttsx-specific options. // ------------------------------------------------------------------------- { name: "--require", aliases: ["-r"], kind: "value", repeatable: true, subcommands: ["ttsx"], consumedBy: ["launcher"], description: "Preload a module before the entrypoint (ttsx; repeatable).", }, { name: "--no-plugins", kind: "boolean", subcommands: ["ttsx"], consumedBy: ["launcher"], description: "Build the project without ttsc plugins (ttsx).", }, // ------------------------------------------------------------------------- // tsgo-internal flags ttsc adds itself; users may also forward them. // Declaring them keeps the launcher's parser from treating them as // unknown forwarded flags whose value token gets misclassified. // ------------------------------------------------------------------------- { name: "--listEmittedFiles", kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["runBuild", "tsgo"], forwardTo: "tsgo", internalShadow: true, description: "Print the list of emitted files (forwarded to tsgo; ttsc keeps the lines when forwarded).", }, { // tsgo declares `--pretty` as `type: boolean`, so it occupies one argv // token and consumes a following one only when that token is the literal // `true` or `false` — the shape the engine's boolean branch implements. // Declaring it `value` made the forwarding path swallow whatever followed, // so `ttsc --pretty a.ts` lost its input file and silently switched to // project mode. name: "--pretty", kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["tsgo"], forwardTo: "tsgo", internalShadow: true, description: "Toggle tsgo pretty-printed diagnostics (forwarded to tsgo).", }, { name: "--diagnostics", kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["runBuild", "tsgo", "lint"], forwardTo: "tsgo", nativeCapability: "diagnosticsTiming", description: "Print compiler performance information; plugin-backed ttsc runs add plugin wall-clock timings.", }, { name: "--extendedDiagnostics", kind: "boolean", subcommands: ["ttsc", "ttsx", "build", "check", "fix", "format"], consumedBy: ["runBuild", "tsgo", "lint"], forwardTo: "tsgo", nativeCapability: "diagnosticsTiming", description: "Print detailed compiler performance information; plugin-backed ttsc runs add plugin wall-clock timings.", }, { name: "--showConfig", kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["tsgo"], forwardTo: "tsgo", terminal: true, description: "Print the resolved tsconfig and exit (forwarded to tsgo).", }, { name: "--listFilesOnly", kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["tsgo"], forwardTo: "tsgo", terminal: true, description: "Print the project file list and exit (forwarded to tsgo).", }, { name: "--all", kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["tsgo"], forwardTo: "tsgo", terminal: true, projectFree: true, description: "Print the full tsgo CLI help and exit.", }, { name: "--init", kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["tsgo"], forwardTo: "tsgo", terminal: true, projectFree: true, description: "Write a starter tsconfig.json and exit (forwarded to tsgo).", }, { // tsgo's short synonym for `--help`. ttsc owns `--help` / `-h` itself (the // launcher prints its own help), so `-?` is declared as its own tsgo-only // row rather than as an alias — that keeps `ttsc -?` printing tsgo's help // while putting the token inside the schema, where the terminal and // project-free classifications are derived from. name: "-?", kind: "boolean", subcommands: ["ttsc", "build", "check"], consumedBy: ["tsgo"], forwardTo: "tsgo", terminal: true, projectFree: true, description: "Print the tsgo CLI help and exit (forwarded to tsgo).", }, // ------------------------------------------------------------------------- // `--tsgo-args` — JSON-encoded passthrough envelope the launcher emits on // the way down to native sidecars. The sidecars decode it back into the // tsgo argv. Listed here so the schema describes every flag the Go layers // accept, not just the user-facing ones. // ------------------------------------------------------------------------- { name: "--tsgo-args", kind: "value", subcommands: [ "ttsc", "build", "check", "fix", "format", "prepare", "clean", ], consumedBy: ["host", "lint"], description: "JSON-encoded tsgo passthrough argv (internal: emitted by runBuild).", }, { name: "--plugins-json", kind: "value", subcommands: ["build", "check", "fix", "format"], consumedBy: ["host", "lint"], description: "JSON-encoded ttsc plugin manifest (internal: emitted by runBuild).", }, { name: "--project-context-json", kind: "value", subcommands: ["build", "check", "fix", "format"], consumedBy: ["lint"], description: "JSON-encoded lexical and physical project identity (internal: emitted by runBuild).", }, { name: "--manifest", kind: "value", subcommands: ["build"], consumedBy: ["host"], description: "Write emitted file list as JSON to this path (host build only).", }, { name: "--file", kind: "value", subcommands: ["build", "check"], consumedBy: ["lint"], description: "Absolute or cwd-relative path of the .ts file to transform (lint transform only).", }, { name: "--out", kind: "value", subcommands: ["build", "check"], consumedBy: ["lint"], description: "Write transform output to PATH (lint transform only; default: stdout).", }, ]; /** * Normalize a CLI token to the identity the compiler ttsc wraps resolves it by: * one or two leading dashes removed, the remainder lower-cased. * * TypeScript's option parser — legacy `tsc` and native tsgo alike — strips a * `--` or `-` prefix and matches the rest case-insensitively, so `--noEmit`, * `--noemit`, `--NOEMIT`, and `-noEmit` all name the same option to the tool * ttsc forwards to. The launcher used to key its index on the exact spelling, * so a case variant of a ttsc-owned flag fell through the unknown-flag escape * hatch: tsgo honoured it and every ttsc-side consumer of the same flag never * fired, with no diagnostic. * * This is the single normalization. Everything that resolves a token against * `FLAG_SCHEMA` — the parsing engine, the terminal / shadow / project-free * classifications, and the generated Go allow-lists — keys off this function, * so no two layers can disagree about which flag a spelling names. */ function normalizeFlagToken(token) { return token.replace(/^--?/, "").toLowerCase(); } /** * Resolve a raw argv token to the flag it names, or `undefined` when the schema * claims no such flag. * * Accepts every spelling the compiler accepts — any casing, one or two leading * dashes — plus the inline `--flag=value` form, whose value is not part of the * identity. A token without a leading dash is never a flag: bare tokens are * input files and flag values, and resolving them here would let a value like * the `all` of `--target all` masquerade as `--all`. */ function resolveFlagSpec(token) { if (!token.startsWith("-")) return undefined; const equalsIndex = token.indexOf("="); const name = equalsIndex === -1 ? token : token.slice(0, equalsIndex); return exports.FLAG_BY_TOKEN.get(normalizeFlagToken(name)); } /** * Lookup of every declared spelling → its canonical FlagSpec, keyed by * {@link normalizeFlagToken}. Built once at module load so the parsing engine * has O(1) flag resolution. Prefer {@link resolveFlagSpec}, which applies the * normalization for the caller. */ exports.FLAG_BY_TOKEN = buildFlagIndex(); function buildFlagIndex() { const index = new Map(); for (const flag of exports.FLAG_SCHEMA) { register(index, flag.name, flag); for (const alias of flag.aliases ?? []) { register(index, alias, flag); } } return index; } function register(index, spelling, flag) { // Two spellings that normalize to one identity would make the schema // ambiguous, so the collision fails loudly at module load rather than // resolving to whichever row was declared last. const key = normalizeFlagToken(spelling); const existing = index.get(key); if (existing && existing !== flag) { throw new Error(`ttsc flag schema: duplicate token ${JSON.stringify(spelling)} claimed by ${existing.name} and ${flag.name}`); } index.set(key, flag); } /** Tokens (canonical name + aliases) accepted in `subcommand`. */ function flagsForSubcommand(subcommand) { return exports.FLAG_SCHEMA.filter((flag) => flag.subcommands.includes(subcommand)); } /** * Allow-list map for the Go layer named `layer` (`"host"` or `"lint"`): * flag-name (no leading dashes) → whether the flag takes a value token. The * generator emits a literal Go map with the same shape, but this function is * the runtime equivalent — used in tests to verify the generated Go matches the * schema. */ function buildGoAllowList(layer) { const out = new Map(); for (const flag of exports.FLAG_SCHEMA) { if (!flag.consumedBy.includes(layer)) continue; const takesValue = flag.kind === "value" || flag.kind === "valueOptional"; for (const name of [flag.name, ...(flag.aliases ?? [])]) { // Keyed by the one normalization the runtime token lookup uses, so the // generated allow-lists and `resolveFlagSpec` cannot recognise different // spellings. The Go consumers apply the same normalization before the // lookup (`strings.ToLower` on the dash-stripped name). const key = normalizeFlagToken(name); // If two flags collide on the normalized key (e.g. `-p` vs `--project`), // use the value-taking shape. const existing = out.get(key); if (existing !== undefined && existing !== takesValue) { throw new Error(`ttsc flag schema: conflicting Go allow-list entry for ${JSON.stringify(key)} on layer ${layer}`); } out.set(key, takesValue); } } return out; } //# sourceMappingURL=schema.js.map