-
Notifications
You must be signed in to change notification settings - Fork 323
Description
Clear and concise description of the problem
Intro
Currently, TypeSpec primarily relies on tspconfig.yaml for configuration. This YAML-based configuration requires TypeSpec to resolve custom linter libraries and dependencies based on string names internally.
This internal resolution process implicitly depends on the existence of a traditional node_modules structure. This creates significant friction and requires workarounds in modern JavaScript runtimes like Deno, which do not create node_modules directories by default.
For example, to use a custom linter in a Deno environment, developers must currently create an explicit workaround involving symbolic links and dummy package.json files within an emulated node_modules structure to satisfy TypeSpec's internal library resolution logic.
Therefore, I propose adding support for JavaScript (tsp.config.js) and TypeScript (tsp.config.ts) configuration files.
This approach, similar to the modern "Flat Config" adopted by ESLint, allows the configuration to be defined as a JavaScript object, enabling the use of standard ESM import statements.
By using import, we delegate library and module resolution to the host JavaScript runtime. This eliminates TypeSpec's need to parse strings and implicitly rely on file system conventions like node_modules.
Example
// tsp.config.ts
import { defineConfig } from "@typespec/compiler";
import bestPractices from "@typespec/best-practices/recommended";
import myCoolLinterRule from "./linter.ts";
import openapi3Emitter from "@typespec/openapi3";
export default defineConfig({
emit: [
openapi3Emitter,
],
options: {
"@typespec/openapi3": {
"emitter-output-dir": "{output-dir}",
"output-file": "{service-name}.{version}.spec.yaml",
},
},
linter: {
extends: [
bestPractices,
myCoolLinterRule
],
},
});
// linter.ts
import { defineLinter } from "@typespec/compiler";
import { requiredDocRule } from "./rules/required-doc.rule.ts";
export const $linter = defineLinter({
...
});in YAML:
emit:
- "@typespec/openapi3"
options:
"@typespec/openapi3":
emitter-output-dir: "{output-dir}"
output-file: "{service-name}.{version}.spec.yaml"
linter:
library:
- "@typespec/best-practices/recommended"Benefits and Rationale
True Runtime Agnosticism: By delegating resolution to the host runtime (via import), TypeSpec achieves better compatibility with diverse environments without requiring file system hacks.
Type Safety for TypeScript: Importing a defineConfig helper allows users to write their configuration with full type safety, IntelliSense, and compilation checks. This is especially useful now that runtimes like Node.js 24+ can execute TypeScript files directly.
Simplified Parser: This change reduces the complexity within TypeSpec's compiler, as the need for a custom YAML parser and string-based module resolution logic is diminished or removed for the JS/TS config path.
Dynamic Configuration: Users gain the ability to use standard JavaScript logic (e.g., conditional statements, environment variables) to dynamically generate their configurations.
Checklist
- Follow our Code of Conduct
- Read the docs.
- Check that there isn't already an issue that request the same feature to avoid creating a duplicate.