From b989a1ef55e90985d11e069eb8140699597e7694 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 27 Jun 2025 12:11:32 -0700 Subject: [PATCH 01/10] MCP server? --- .github/copilot-instructions.md | 291 +------- .github/copilot-library.md | 165 +++++ .github/copilot-questions.md | 4 + package-lock.json | 1125 ++++++++++++++++++++++++++++++- package.json | 1 + 5 files changed, 1310 insertions(+), 276 deletions(-) create mode 100644 .github/copilot-library.md create mode 100644 .github/copilot-questions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 47229f1f7c791..d9df1fbb6a488 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,226 +1,31 @@ -# Guide for Copilot +## Documentation MCP server -This document provides a concise guide for writing TypeScript fourslash tests and compiler tests, along with build instructions. +The repo is large and the project is complex. +This document contains information you'll need no matter what the task is, but we have a separate library of content that you'll need to consult depending on the issue. -## Build Instructions Summary +You have been given the `md-fragments` MCP server. +Before doing anything, query its `list-topics` endpoint and remember all the topics you saw. +Consult these topics often, as they contain important information relevant to the work. -### Setup -1. Install Node.js (current or LTS) -2. Clone the repository: `git clone --depth=1 https://github.com/microsoft/TypeScript` -3. Install dependencies: `npm ci` +Before you end a session, read the `How to Finish a Session` topic. -### Common Build Tasks -```bash -npx hereby local # Build the compiler into built/local -npx hereby clean # Delete the built compiler -npx hereby tests # Build the test infrastructure -npx hereby runtests # Run all tests -npx hereby runtests-parallel # Run tests in parallel (recommended) -npx hereby runtests --runner=fourslash # Run only fourslash tests -npx hereby runtests --runner=compiler # Run only compiler tests -npx hereby runtests --tests= # Run specific test -npx hereby baseline-accept # Accept new test baselines -npx hereby lint # Run eslint -npx hereby format # Run code formatting -``` - -## Fourslash Test Syntax Guide - -Fourslash tests are interactive TypeScript language service tests. They validate IDE features like completions, quick info, navigation, and refactoring. - -### Basic Structure -```typescript -/// - -////code goes here with /*markers*/ - -// Test assertions go here -``` - -### Key Syntax Elements - -#### 1. Source Code Definition -Use `////` to define source code lines: -```typescript -////function foo(x: number) { -//// return x + 1; -////} -////let result = foo(/*marker*/42); -``` - -#### 2. Markers for Positioning -Use `/**/` for anonymous markers or `/*name*/` for named markers: -```typescript -////let x = /*1*/someValue; -////let y = /*cursor*/anotherValue; -``` - -#### 3. Multi-file Tests -Use `// @Filename:` to define multiple files: -```typescript -// @Filename: /a.ts -////export const value = 42; - -// @Filename: /b.ts -////import { value } from './a'; -////console.log(/*marker*/value); -``` - -#### 4. Ranges -Use `[|text|]` to define text ranges: -```typescript -////function test() { -//// [|return 42;|] -////} -``` - -### Common API Patterns - -#### Navigation & Positioning -```typescript -goTo.marker("markerName"); // Navigate to marker -goTo.marker(); // Navigate to anonymous marker /**/ -``` - -#### Verification (Prefer these over baselines) -```typescript -verify.currentLineContentIs("expected content"); -verify.completions({ includes: "itemName" }); -verify.completions({ excludes: "itemName" }); -verify.quickInfoIs("expected info"); -verify.codeFix({ - description: "Fix description", - newFileContent: "expected content after fix" -}); -``` - -#### Completions Testing -```typescript -verify.completions({ - marker: "1", - includes: { name: "foo", source: "/a", hasAction: true }, - isNewIdentifierLocation: true, - preferences: { includeCompletionsForModuleExports: true } -}); -``` - -#### Code Fixes Testing -```typescript -verify.codeFix({ - description: "Add missing property", - index: 0, - newFileContent: `class C { - property: string; - method() { this.property = "value"; } -}` -}); -``` - -#### Formatting -```typescript -format.document(); -verify.currentLineContentIs("formatted content"); -``` - -### Simple Example -```typescript -/// - -////interface User { -//// name: string; -////} -//// -////const user: User = { -//// /*completion*/ -////}; - -verify.completions({ - marker: "completion", - includes: { name: "name", sortText: "0" } -}); -``` - -## Compiler Test Syntax Guide +## Asking Questions -Compiler tests validate TypeScript compilation behavior, type checking, and error reporting. +We want to make you smarter over time. +If you encounter a situation where you think a developer on this project would be able to provide a useful answer *and* it's not something offered by the Documentation MCP server, add a question to the file `.github/copilot-questions.md` +Explain what you searched for so that we can put the right search terms in the documentation library. -### Basic Structure -- Simple `.ts` files in `tests/cases/compiler/` -- Use comments to indicate expected behavior -- No special test harness - just TypeScript code +## Common Commands -### Compiler Directives -Use `// @directive: value` for compiler options: -```typescript -// @strict: true -// @target: ES2015 -// @lib: ES2015,DOM +You'll need to run these commands often. Always use `npx` to run `hereby` commands. -let x: string = 42; // Error expected -``` - -### Common Directives -```typescript -// @strict: true/false -// @noImplicitAny: true/false -// @target: ES5/ES2015/ES2020/ESNext -// @module: commonjs/amd/es6/esnext -// @lib: ES5,DOM/ES2015/ES2020 -// @declaration: true/false -// @skipLibCheck: true/false -``` - -### Multi-file Tests -```typescript -// @Filename: helper.ts -export function helper(x: number): string { - return x.toString(); -} - -// @Filename: main.ts -import { helper } from "./helper"; -const result = helper(42); -``` - -### Error Expectations -Use comments to document expected behavior: -```typescript -abstract class Base { - abstract method(): void; -} - -class Derived extends Base { - // Missing implementation - should error -} - -new Base(); // Should error - cannot instantiate abstract class -``` - -### Type Testing Patterns -```typescript -// Test type inference -let inferred = [1, 2, 3]; // Should infer number[] - -// Test type compatibility -type A = { x: number }; -type B = { x: number; y: string }; -let a: A = { x: 1 }; -let b: B = { x: 1, y: "hello" }; -a = b; // Should work - B is assignable to A -b = a; // Should error - A missing property y -``` - -### Simple Example -```typescript -// Test that optional properties work correctly -interface Config { - required: string; - optional?: number; -} - -const config1: Config = { required: "test" }; // Should work -const config2: Config = { required: "test", optional: 42 }; // Should work -const config3: Config = { optional: 42 }; // Should error - missing required +```bash +npx hereby local # Build the compiler into built/local +npx hereby runtests-parallel # Run all tests; this will take 10-15 minutes +npx hereby runtests -t # Run testcases matching a specific pattern +npx hereby baseline-accept # Accept new test baselines +npx hereby lint # Run lint. Always do this before submitting +npx hereby format # Run code formatting. Always do this before submitting ``` ## Test Writing Best Practices @@ -243,61 +48,11 @@ const config3: Config = { optional: 42 }; // Should error - missing required 3. **Start simple** - Begin with the most basic case of a feature 4. **Test edge cases** - Include boundary conditions and error scenarios -## Running Specific Tests - -```bash -# Run a specific fourslash test -npx hereby runtests --tests=tests/cases/fourslash/completionForObjectProperty.ts - -# Run a specific compiler test -npx hereby runtests --tests=tests/cases/compiler/abstractClassUnionInstantiation.ts - -# Run tests matching a pattern -npx hereby runtests --tests=tests/cases/fourslash/completion*.ts -``` - -## Important Guidelines - -### Keeping Things Tidy - -- Once you think you're done, run `npx hereby lint` and fix any issues -- Then always run `npx hereby format` as your last step - -### Test Locations - -- Only add testcases in `tests/cases/compiler` or `tests/cases/fourslash` -- Filenames in `tests/cases/compiler` must always end with `.ts`, not `.d.ts` -- Do not write direct unit tests as they are almost never the correct test format for our repo - -### Performance Expectations - -- Running a set of tests may take up to 4 minutes -- A full test run may take up to 15 minutes - -### Working with Issues +## Understanding the Assigned Task - Maintainer comments in the issue should generally take priority over OP's comments - Maintainers might give you hints on where to start. They are not always right, but a good place to start -### Debugging Tips - -printf debugging is going to be very useful as you are figuring things out. -To do this, use `console.log`, but you'll need to `ts-ignore` it. -Write something like this: -```ts,diff -function checkSomething(n: Node) { - doSomething(n); -+ // @ts-ignore DEBUG CODE ONLY, REMOVE ME WHEN DONE -+ console.log(`Got node with pos = ${n.pos}`); - doSomethingElse(n); -} -``` -We have a lot of enums so you might want to print back their symbolic name, to do this, index back into the name of the enum -```ts - // @ts-ignore DEBUG CODE ONLY, REMOVE ME WHEN DONE - console.log(`Got node with kind = ${SyntaxKind[n.kind]}`); -``` - ## Recommended Workflow When fixing bugs or implementing features, follow this workflow: @@ -321,7 +76,3 @@ When fixing bugs or implementing features, follow this workflow: - Run `npx hereby runtests-parallel` and wait for it to finish (10-15 minutes is normal!) - Some collateral baseline changes are normal, but review for correctness - Put these diffs in another commit - -6. **Always format and lint** - - Don't forget to run `npx hereby lint` and `npx hereby format` before you're done - - Double-check your line endings. Source files in this repo typically use CRLF line endings. Fix all line endings to be consistent before you wrap up diff --git a/.github/copilot-library.md b/.github/copilot-library.md new file mode 100644 index 0000000000000..971877ca9afb7 --- /dev/null +++ b/.github/copilot-library.md @@ -0,0 +1,165 @@ +# How to Finish a Session + +> Information about mandatory tasks to do at the end of a session + +When you think you're done, there are some mandatory things you need to do before finishing your task and completing your fix. + +If you haven't already, run `hereby runtests-parallel` and ensure there are zero errors. Read the sections on Baseline Tests if you don't know how to manage the output of baseline diffs. + +Next, run `hereby lint`. Fix any errors reported here. Lint errors will generally never cause new test failures to appear. + +Last, run `hereby format`. This will ensure your code matches the repo coding guidelines. + +# How to Debug + +> Tips and tricks for how to debug code + +printf debugging is going to be very useful as you are figuring things out. +To do this, use `console.log`, but you'll need to `ts-ignore` it. +Write something like this: +```ts,diff +function checkSomething(n: Node) { + doSomething(n); ++ // @ts-ignore DEBUG CODE ONLY, REMOVE ME WHEN DONE ++ console.log(`Got node with pos = ${n.pos}`); + doSomethingElse(n); +} +``` + +We have a lot of enums so you might want to print back their symbolic name, to do this, index back into the name of the enum +```ts + // @ts-ignore DEBUG CODE ONLY, REMOVE ME WHEN DONE + console.log(`Got node with kind = ${SyntaxKind[n.kind]}`); +``` + +If you run a specific test using `hereby runtests -t testName`, you will see the console output from these `log` calls. + +# Compiler Tests + +> How to work with baseline-based tests like those in `tests/cases/compiler` + +In general, all testcases you add related to core checker behavior should be in the form of baseline tests. These tests validate TypeScript behavior, type checking, symbol resolution, and error reporting. When these tests run, they either pass (because the output matches the reference copy in `tests/baselines/reference`), or fail and create a new file in `tests/baselines/local`. + +The failure when you create a new test is expected; for new content, example the baseline output to see that it matches what you expect, and run `hereby baseline-accept` to copy the new file to `tests/baselines/reference` + +When this failure occurs in other situations, compare the new file to the old file and make sure it's expected, and run `hereby baseline-accept`. This will generate a diff you should commit as part of your changes. + +## Creating + +> How to create a new compiler test, and its syntax + +Put a new file in `tests/cases/compiler` using a descriptive (but reasonably short) filename. The file extension must be `.ts`, you should never check in a file named `.d.ts` here. + +## Test File Syntax + +> Syntax for how to set options and filenames in compiler baseline tests + +The file format looks like this +```ts +// @strict: true +// @target: ES2015 +// @lib: ES2015,DOM + +let x: string = 42; // Error expected +``` +You can set any TypeScript compiler option using the `// @flag: value` syntax. + +The default file extension for the interior file is `.ts`, but you can change that (or create multiple files) with filename directives: +```ts +// @Filename: helper.tsx +export function helper(x: number): string { + let x =
; + return x.toString(); +} + +// @Filename: main.ts +import { helper } from "./helper"; +const result = helper(42); +``` + +Use comments to document expected behavior: +```ts +abstract class Base { + abstract method(): void; +} + +class Derived extends Base { + // Missing implementation - should error +} + +new Base(); // Should error - cannot instantiate abstract class +``` + + +# Fourslash Testing + +> Fourslash is our testing system for language service functionality + +Fourslash tests are interactive TypeScript language service tests. They validate IDE features like completions, quick info, navigation, and refactoring. You create a new fourslash test by putting a file in `tests/cases/fourslash`. + +They have a "user code" section, prefixed by four slashes per line, followed by one or more instructions for what to do with the code. Within the code, a `/**/` comment creates an anonymous "marker"; named markers use alphanumeric text between the stars (`/*here*/`). You can use the markers to refer to specific positions in the code: +```typescript +////function foo(x: number) { +//// return x + 1; +////} +////let result = foo(/**/42); + +goTo.marker(); +verify.baselineSignatureHelp(); +``` + +Use `// @Filename:` to define multiple files: +```typescript +// @Filename: /a.ts +////export const value = 42; + +// @Filename: /b.ts +////import { value } from './a'; +////console.log(/*marker*/value); +``` + +Use `[|text|]` to define text ranges, which can be used for selecting text or describing expected Find All References results. +```typescript +////function test() { +//// [|return 42;|] +////} +``` + +More code examples: +```typescript +// Moving the virtual caret around +goTo.marker("markerName"); // Navigate to marker +goTo.marker(); // Navigate to anonymous marker /**/ + +// Verifying expected results (generally preferred over baselines in these tests) +verify.currentLineContentIs("expected content"); +verify.completions({ includes: "itemName" }); +verify.completions({ excludes: "itemName" }); +verify.quickInfoIs("expected info"); +verify.codeFix({ + description: "Fix description", + newFileContent: "expected content after fix" +}); + +// Completions testing +verify.completions({ + marker: "1", + includes: { name: "foo", source: "/a", hasAction: true }, + isNewIdentifierLocation: true, + preferences: { includeCompletionsForModuleExports: true } +}); + +// Code fixes testing +verify.codeFix({ + description: "Add missing property", + index: 0, + newFileContent: `class C { + property: string; + method() { this.property = "value"; } +}` +}); + +// Formatting +format.document(); +verify.currentLineContentIs("formatted content"); +``` diff --git a/.github/copilot-questions.md b/.github/copilot-questions.md new file mode 100644 index 0000000000000..250d4bf2d776d --- /dev/null +++ b/.github/copilot-questions.md @@ -0,0 +1,4 @@ +Questions I have that I think the developers of this project can help me with: + * How does control flow analysis represent a circular graph? I checked the documentation server for "cfa" and "control flow" + * How do I tell if a symbol is in the global scope? I checked the documentation server for topics referencing "symbol" and "global" + * What is an `EscapedName`, exactly? diff --git a/package-lock.json b/package-lock.json index d4d2c055ccfff..5b2839c2db35f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@esfx/canceltoken": "^1.0.0", "@eslint/js": "^9.20.0", "@octokit/rest": "^21.1.1", + "@ryancavanaugh/mcp-server-md-fragments": "^0.0.1", "@types/chai": "^4.3.20", "@types/diff": "^7.0.1", "@types/minimist": "^1.2.5", @@ -901,6 +902,29 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.13.2.tgz", + "integrity": "sha512-Vx7qOcmoKkR3qhaQ9qf3GxiVKCEu+zfJddHv6x3dY/9P6+uIwJnmuAur5aB+4FDXf41rRrDnOEGkviX5oYZ67w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1100,6 +1124,21 @@ "node": ">=14" } }, + "node_modules/@ryancavanaugh/mcp-server-md-fragments": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@ryancavanaugh/mcp-server-md-fragments/-/mcp-server-md-fragments-0.0.1.tgz", + "integrity": "sha512-wSY4KJ+4+u1eahjOBEhyfjlYYcQ0B3biGmpxC8I/m7smMFejLU4neM4OGvzmI+jZDoISL3+EgGw45C331OKpvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.0.0", + "marked": "^15.0.12", + "zod": "^3.25.67" + }, + "bin": { + "mcp-server-md-fragments": "dist/cli.js" + } + }, "node_modules/@snyk/github-codeowners": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@snyk/github-codeowners/-/github-codeowners-1.1.0.tgz", @@ -1397,6 +1436,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -1590,6 +1643,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1623,6 +1697,16 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/c8": { "version": "10.1.3", "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", @@ -1983,12 +2067,69 @@ "integrity": "sha512-+mecFacaFxGl+1G31IsCx41taUXuW2FxX+4xIE0TIPhgML+Jb9JFcBWGhhWerd1/vhScubdmHqTwOhB0KCUUAg==", "dev": true }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2087,6 +2228,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/des.js": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", @@ -2159,6 +2310,13 @@ "wcwidth": "^1.0.1" } }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, "node_modules/eight-colors": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/eight-colors/-/eight-colors-1.3.1.tgz", @@ -2171,6 +2329,16 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", @@ -2263,6 +2431,13 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2501,6 +2676,98 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz", + "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, "node_modules/fast-content-type-parse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", @@ -2623,6 +2890,24 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2683,6 +2968,26 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2902,6 +3207,46 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2962,6 +3307,16 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/irregular-plurals": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", @@ -3031,6 +3386,13 @@ "node": ">=8" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -3378,6 +3740,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -3387,6 +3762,29 @@ "node": ">= 0.4" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3421,6 +3819,29 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -3754,6 +4175,16 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3763,6 +4194,16 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -3775,6 +4216,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -3876,6 +4330,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3910,6 +4374,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -3937,6 +4411,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/playwright": { "version": "1.50.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", @@ -4006,6 +4490,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4059,6 +4557,32 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -4134,6 +4658,23 @@ "node": ">=0.10.0" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4177,6 +4718,13 @@ } ] }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/scslre": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", @@ -4203,6 +4751,29 @@ "node": ">=10" } }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -4212,6 +4783,29 @@ "randombytes": "^2.1.0" } }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -4348,6 +4942,16 @@ "source-map": "^0.6.0" } }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4524,6 +5128,16 @@ "node": ">=8.0" } }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/ts-api-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", @@ -4572,6 +5186,21 @@ "node": ">=4" } }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typed-rest-client": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-2.1.0.tgz", @@ -4650,6 +5279,16 @@ "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", "dev": true }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4673,6 +5312,16 @@ "node": ">=10.12.0" } }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -4907,14 +5556,25 @@ } }, "node_modules/zod": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "version": "3.25.67", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", + "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/zod-validation-error": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz", @@ -5411,6 +6071,25 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@modelcontextprotocol/sdk": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.13.2.tgz", + "integrity": "sha512-Vx7qOcmoKkR3qhaQ9qf3GxiVKCEu+zfJddHv6x3dY/9P6+uIwJnmuAur5aB+4FDXf41rRrDnOEGkviX5oYZ67w==", + "dev": true, + "requires": { + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5560,6 +6239,17 @@ "dev": true, "optional": true }, + "@ryancavanaugh/mcp-server-md-fragments": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@ryancavanaugh/mcp-server-md-fragments/-/mcp-server-md-fragments-0.0.1.tgz", + "integrity": "sha512-wSY4KJ+4+u1eahjOBEhyfjlYYcQ0B3biGmpxC8I/m7smMFejLU4neM4OGvzmI+jZDoISL3+EgGw45C331OKpvQ==", + "dev": true, + "requires": { + "@modelcontextprotocol/sdk": "^1.0.0", + "marked": "^15.0.12", + "zod": "^3.25.67" + } + }, "@snyk/github-codeowners": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@snyk/github-codeowners/-/github-codeowners-1.1.0.tgz", @@ -5761,6 +6451,16 @@ } } }, + "accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "requires": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + } + }, "acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -5899,6 +6599,23 @@ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true }, + "body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "dev": true, + "requires": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + } + }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -5929,6 +6646,12 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, "c8": { "version": "10.1.3", "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", @@ -6195,12 +6918,49 @@ "integrity": "sha512-+mecFacaFxGl+1G31IsCx41taUXuW2FxX+4xIE0TIPhgML+Jb9JFcBWGhhWerd1/vhScubdmHqTwOhB0KCUUAg==", "dev": true }, + "content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true + }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true + }, + "cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -6269,6 +7029,12 @@ "clone": "^1.0.2" } }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, "des.js": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", @@ -6329,6 +7095,12 @@ "wcwidth": "^1.0.1" } }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, "eight-colors": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/eight-colors/-/eight-colors-1.3.1.tgz", @@ -6341,6 +7113,12 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true + }, "enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", @@ -6411,6 +7189,12 @@ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -6576,6 +7360,69 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "requires": { + "eventsource-parser": "^3.0.1" + } + }, + "eventsource-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz", + "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==", + "dev": true + }, + "express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "dev": true, + "requires": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + } + }, + "express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "dev": true, + "requires": {} + }, "fast-content-type-parse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", @@ -6666,6 +7513,20 @@ "to-regex-range": "^5.0.1" } }, + "finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "dev": true, + "requires": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + } + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -6708,6 +7569,18 @@ "signal-exit": "^4.0.1" } }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6860,6 +7733,36 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6904,6 +7807,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, "irregular-plurals": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", @@ -6952,6 +7861,12 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, + "is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -7199,12 +8114,30 @@ "semver": "^7.5.3" } }, + "marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "dev": true + }, "math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true }, + "media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true + }, + "merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -7229,6 +8162,21 @@ } } }, + "mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true + }, + "mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dev": true, + "requires": { + "mime-db": "^1.54.0" + } + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -7484,18 +8432,39 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, "object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -7567,6 +8536,12 @@ "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", "dev": true }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7589,6 +8564,12 @@ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, + "path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true + }, "pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -7607,6 +8588,12 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true }, + "pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "dev": true + }, "playwright": { "version": "1.50.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", @@ -7647,6 +8634,16 @@ "parse-ms": "^3.0.0" } }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7677,6 +8674,24 @@ "safe-buffer": "^5.1.0" } }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + } + }, "readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -7726,6 +8741,19 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, + "router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "requires": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7741,6 +8769,12 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, "scslre": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", @@ -7758,6 +8792,25 @@ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true }, + "send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "dev": true, + "requires": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + } + }, "serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", @@ -7767,6 +8820,24 @@ "randombytes": "^2.1.0" } }, + "serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "dev": true, + "requires": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7858,6 +8929,12 @@ "source-map": "^0.6.0" } }, + "statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true + }, "string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -7988,6 +9065,12 @@ "is-number": "^7.0.0" } }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, "ts-api-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", @@ -8022,6 +9105,17 @@ "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true }, + "type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "requires": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + } + }, "typed-rest-client": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-2.1.0.tgz", @@ -8076,6 +9170,12 @@ "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", "dev": true }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -8096,6 +9196,12 @@ "convert-source-map": "^2.0.0" } }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, "wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -8275,11 +9381,18 @@ "dev": true }, "zod": { - "version": "3.24.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", - "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", + "version": "3.25.67", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", + "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", "dev": true }, + "zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, + "requires": {} + }, "zod-validation-error": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz", diff --git a/package.json b/package.json index ef2c80f8b91c2..fdb21ffb792ac 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@esfx/canceltoken": "^1.0.0", "@eslint/js": "^9.20.0", "@octokit/rest": "^21.1.1", + "@ryancavanaugh/mcp-server-md-fragments": "^0.0.1", "@types/chai": "^4.3.20", "@types/diff": "^7.0.1", "@types/minimist": "^1.2.5", From b481a4229f62a32b7a735fa35f12b6d558dbfc89 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 27 Jun 2025 21:36:39 -0700 Subject: [PATCH 02/10] Update copilot-instructions.md --- .github/copilot-instructions.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d9df1fbb6a488..1a72843407e85 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -6,6 +6,9 @@ This document contains information you'll need no matter what the task is, but w You have been given the `md-fragments` MCP server. Before doing anything, query its `list-topics` endpoint and remember all the topics you saw. Consult these topics often, as they contain important information relevant to the work. +MOST OPERATIONS (adding tests, running them, etc) HAVE RELEVANT DOCUMENTATION. + +YOU MUST FOLLOW DIRECTIONS FROM THE "HOW TO FINISH A SESSION" TOPIC BEFORE FINISHING A PULL REQUEST Before you end a session, read the `How to Finish a Session` topic. @@ -15,6 +18,8 @@ We want to make you smarter over time. If you encounter a situation where you think a developer on this project would be able to provide a useful answer *and* it's not something offered by the Documentation MCP server, add a question to the file `.github/copilot-questions.md` Explain what you searched for so that we can put the right search terms in the documentation library. +IT'S A BAD FORM IF YOU DON'T ASK AT LEAST ONE QUESTION, IT SHOWS YOU ARE NOT LEVERAGING THE INSTRUCTIONS. + ## Common Commands You'll need to run these commands often. Always use `npx` to run `hereby` commands. From fbaed54eadd65346edbac63494e1b9ffae5c73af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 04:36:59 +0000 Subject: [PATCH 03/10] Initial plan From f4f38c0172f5ce226f9e27f8d0faefa43b41c8d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 04:42:06 +0000 Subject: [PATCH 04/10] Initial investigation of JSX comment duplication issue Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- tests/cases/compiler/jsxCommentDuplication.tsx | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/cases/compiler/jsxCommentDuplication.tsx diff --git a/tests/cases/compiler/jsxCommentDuplication.tsx b/tests/cases/compiler/jsxCommentDuplication.tsx new file mode 100644 index 0000000000000..fe13875536038 --- /dev/null +++ b/tests/cases/compiler/jsxCommentDuplication.tsx @@ -0,0 +1,3 @@ +// @jsx: preserve +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; \ No newline at end of file From 2aa5757d064c8e62e31a9aa714beb80ba95607ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 04:53:12 +0000 Subject: [PATCH 05/10] Fix attempt for JSX comment duplication - not working yet Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 61 +++++++++++++++---- .../compiler/jsxCommentDuplicationDebug.tsx | 3 + 2 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 tests/cases/compiler/jsxCommentDuplicationDebug.tsx diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c60fd92787275..d679fafabbad0 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -4713,9 +4713,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri * * NOTE: You probably don't want to call this directly and should be using `emitList` or `emitExpressionList` instead. */ - function emitNodeListItems(emit: EmitFunction, parentNode: Node | undefined, children: readonly Child[], format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start: number, count: number, hasTrailingComma: boolean, childrenTextRange: TextRange | undefined) { - // Write the opening line terminator or leading whitespace. - const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; + function emitNodeListItems(emit: EmitFunction, parentNode: Node | undefined, children: readonly Child[], format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start: number, count: number, hasTrailingComma: boolean, childrenTextRange: TextRange | undefined) { + // Write the opening line terminator or leading whitespace. + const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; let shouldEmitInterveningComments = mayEmitInterveningComments; const leadingLineTerminatorCount = getLeadingLineTerminatorCount(parentNode, children[start], format); @@ -4772,9 +4772,25 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri shouldDecreaseIndentAfterEmit = true; } - if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); + if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { + const commentRange = getCommentRange(child); + + // Check if this is a variable declaration with JSX content that would duplicate comments + let shouldEmitComments = true; + if (child.kind === SyntaxKind.VariableDeclaration && (child as any as VariableDeclaration).initializer) { + const initializer = (child as any as VariableDeclaration).initializer!; + if (initializer.kind === SyntaxKind.JsxElement || + initializer.kind === SyntaxKind.JsxSelfClosingElement || + initializer.kind === SyntaxKind.JsxFragment) { + // @ts-ignore + console.log(`DEBUG: Skipping DelimitersMask comment emission for JSX variable declaration`); + shouldEmitComments = false; + } + } + + if (shouldEmitComments) { + emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); + } } writeLine(separatingLineTerminatorCount); @@ -4785,13 +4801,32 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } } - // Emit this child. - if (shouldEmitInterveningComments) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos); - } - else { - shouldEmitInterveningComments = mayEmitInterveningComments; + // Emit this child. + if (shouldEmitInterveningComments) { + const commentRange = getCommentRange(child); + + // Check if this is a variable declaration with JSX content that would duplicate comments + let shouldEmitComments = true; + if (child.kind === SyntaxKind.VariableDeclaration && (child as any as VariableDeclaration).initializer) { + const initializer = (child as any as VariableDeclaration).initializer!; + // @ts-ignore + console.log(`DEBUG: VariableDeclaration initializer kind: ${initializer.kind}, SyntaxKind.JsxElement: ${SyntaxKind.JsxElement}`); + // If the initializer is a JSX element, the JSX emitter will handle its own comments + if (initializer.kind === SyntaxKind.JsxElement || + initializer.kind === SyntaxKind.JsxSelfClosingElement || + initializer.kind === SyntaxKind.JsxFragment) { + // @ts-ignore + console.log(`DEBUG: Skipping comment emission for JSX variable declaration`); + shouldEmitComments = false; + } + } + + if (shouldEmitComments) { + emitTrailingCommentsOfPosition(commentRange.pos); + } + } + else { + shouldEmitInterveningComments = mayEmitInterveningComments; } nextListElementPos = child.pos; diff --git a/tests/cases/compiler/jsxCommentDuplicationDebug.tsx b/tests/cases/compiler/jsxCommentDuplicationDebug.tsx new file mode 100644 index 0000000000000..e18a78c5279bb --- /dev/null +++ b/tests/cases/compiler/jsxCommentDuplicationDebug.tsx @@ -0,0 +1,3 @@ +// @jsx: preserve +function App() {} +const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; \ No newline at end of file From 804dddcbf8c77eb697cd085565a4f25a03c83c6d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 05:04:11 +0000 Subject: [PATCH 06/10] Fix JSX comment duplication issue Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 93 ++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 53 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d679fafabbad0..9c86ad24e5778 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1245,13 +1245,15 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri var mostRecentlyAddedSourceMapSource: SourceMapSource; var mostRecentlyAddedSourceMapSourceIndex = -1; - // Comments - var containerPos = -1; - var containerEnd = -1; - var declarationListContainerEnd = -1; - var currentLineMap: readonly number[] | undefined; - var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; - var hasWrittenComment = false; + // Comments + var containerPos = -1; + var containerEnd = -1; + var declarationListContainerEnd = -1; + // Track JSX elements to prevent duplicate comment emission + var currentJsxElement: JsxElement | undefined; + var currentLineMap: readonly number[] | undefined; + var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; + var hasWrittenComment = false; var commentsDisabled = !!printerOptions.removeComments; var lastSubstitution: Node | undefined; var currentParenthesizerRule: ParenthesizerRule | undefined; @@ -3857,10 +3859,15 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // JSX // - function emitJsxElement(node: JsxElement) { - emit(node.openingElement); - emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); - emit(node.closingElement); + function emitJsxElement(node: JsxElement) { + const savedJsxElement = currentJsxElement; + currentJsxElement = node; + + emit(node.openingElement); + emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); + emit(node.closingElement); + + currentJsxElement = savedJsxElement; } function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { @@ -4774,23 +4781,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { const commentRange = getCommentRange(child); - - // Check if this is a variable declaration with JSX content that would duplicate comments - let shouldEmitComments = true; - if (child.kind === SyntaxKind.VariableDeclaration && (child as any as VariableDeclaration).initializer) { - const initializer = (child as any as VariableDeclaration).initializer!; - if (initializer.kind === SyntaxKind.JsxElement || - initializer.kind === SyntaxKind.JsxSelfClosingElement || - initializer.kind === SyntaxKind.JsxFragment) { - // @ts-ignore - console.log(`DEBUG: Skipping DelimitersMask comment emission for JSX variable declaration`); - shouldEmitComments = false; - } - } - - if (shouldEmitComments) { - emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); - } + emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); } writeLine(separatingLineTerminatorCount); @@ -4804,26 +4795,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // Emit this child. if (shouldEmitInterveningComments) { const commentRange = getCommentRange(child); - - // Check if this is a variable declaration with JSX content that would duplicate comments - let shouldEmitComments = true; - if (child.kind === SyntaxKind.VariableDeclaration && (child as any as VariableDeclaration).initializer) { - const initializer = (child as any as VariableDeclaration).initializer!; - // @ts-ignore - console.log(`DEBUG: VariableDeclaration initializer kind: ${initializer.kind}, SyntaxKind.JsxElement: ${SyntaxKind.JsxElement}`); - // If the initializer is a JSX element, the JSX emitter will handle its own comments - if (initializer.kind === SyntaxKind.JsxElement || - initializer.kind === SyntaxKind.JsxSelfClosingElement || - initializer.kind === SyntaxKind.JsxFragment) { - // @ts-ignore - console.log(`DEBUG: Skipping comment emission for JSX variable declaration`); - shouldEmitComments = false; - } - } - - if (shouldEmitComments) { - emitTrailingCommentsOfPosition(commentRange.pos); - } + emitTrailingCommentsOfPosition(commentRange.pos); } else { shouldEmitInterveningComments = mayEmitInterveningComments; @@ -6136,11 +6108,26 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } } - function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { - // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments - if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { - forEachTrailingCommentRange(currentSourceFile.text, end, cb); - } + function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { + // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments + + // Check if this position is within a JSX element that contains comment-only text children + // If so, skip emission as the JSX processor will handle these comments + if (currentJsxElement && end >= currentJsxElement.pos && end <= currentJsxElement.end) { + // Check if any of the JSX children are comment-only text nodes + const hasCommentOnlyText = currentJsxElement.children.some(child => + child.kind === SyntaxKind.JsxText && + (child as JsxText).text.trim().startsWith('/*') && + (child as JsxText).text.trim().endsWith('*/') + ); + if (hasCommentOnlyText) { + return; // Skip comment emission - will be handled by JSX processing + } + } + + if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { + forEachTrailingCommentRange(currentSourceFile.text, end, cb); + } } function hasDetachedComments(pos: number) { From 68b447519446d1263a8db8ff7c3c0c3ab1ddef20 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 05:13:03 +0000 Subject: [PATCH 07/10] Final cleanup and lint fixes for JSX comment duplication fix Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 103 +++++++++--------- .../reference/jsxCommentDuplication.js | 9 ++ .../reference/jsxCommentDuplication.symbols | 11 ++ .../reference/jsxCommentDuplication.types | 17 +++ .../reference/jsxCommentDuplicationDebug.js | 9 ++ .../jsxCommentDuplicationDebug.symbols | 11 ++ .../jsxCommentDuplicationDebug.types | 17 +++ .../compiler/jsxCommentDuplicationDebug.tsx | 3 - 8 files changed, 126 insertions(+), 54 deletions(-) create mode 100644 tests/baselines/reference/jsxCommentDuplication.js create mode 100644 tests/baselines/reference/jsxCommentDuplication.symbols create mode 100644 tests/baselines/reference/jsxCommentDuplication.types create mode 100644 tests/baselines/reference/jsxCommentDuplicationDebug.js create mode 100644 tests/baselines/reference/jsxCommentDuplicationDebug.symbols create mode 100644 tests/baselines/reference/jsxCommentDuplicationDebug.types delete mode 100644 tests/cases/compiler/jsxCommentDuplicationDebug.tsx diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 9c86ad24e5778..ec7e918daa048 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1245,15 +1245,15 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri var mostRecentlyAddedSourceMapSource: SourceMapSource; var mostRecentlyAddedSourceMapSourceIndex = -1; - // Comments - var containerPos = -1; - var containerEnd = -1; - var declarationListContainerEnd = -1; - // Track JSX elements to prevent duplicate comment emission - var currentJsxElement: JsxElement | undefined; - var currentLineMap: readonly number[] | undefined; - var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; - var hasWrittenComment = false; + // Comments + var containerPos = -1; + var containerEnd = -1; + var declarationListContainerEnd = -1; + // Track JSX elements to prevent duplicate comment emission + var currentJsxElement: JsxElement | undefined; + var currentLineMap: readonly number[] | undefined; + var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; + var hasWrittenComment = false; var commentsDisabled = !!printerOptions.removeComments; var lastSubstitution: Node | undefined; var currentParenthesizerRule: ParenthesizerRule | undefined; @@ -3859,15 +3859,15 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // JSX // - function emitJsxElement(node: JsxElement) { - const savedJsxElement = currentJsxElement; - currentJsxElement = node; - - emit(node.openingElement); - emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); - emit(node.closingElement); - - currentJsxElement = savedJsxElement; + function emitJsxElement(node: JsxElement) { + const savedJsxElement = currentJsxElement; + currentJsxElement = node; + + emit(node.openingElement); + emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); + emit(node.closingElement); + + currentJsxElement = savedJsxElement; } function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { @@ -4720,9 +4720,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri * * NOTE: You probably don't want to call this directly and should be using `emitList` or `emitExpressionList` instead. */ - function emitNodeListItems(emit: EmitFunction, parentNode: Node | undefined, children: readonly Child[], format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start: number, count: number, hasTrailingComma: boolean, childrenTextRange: TextRange | undefined) { - // Write the opening line terminator or leading whitespace. - const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; + function emitNodeListItems(emit: EmitFunction, parentNode: Node | undefined, children: readonly Child[], format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start: number, count: number, hasTrailingComma: boolean, childrenTextRange: TextRange | undefined) { + // Write the opening line terminator or leading whitespace. + const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0; let shouldEmitInterveningComments = mayEmitInterveningComments; const leadingLineTerminatorCount = getLeadingLineTerminatorCount(parentNode, children[start], format); @@ -4779,9 +4779,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri shouldDecreaseIndentAfterEmit = true; } - if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); + if (shouldEmitInterveningComments && format & ListFormat.DelimitersMask && !positionIsSynthesized(child.pos)) { + const commentRange = getCommentRange(child); + emitTrailingCommentsOfPosition(commentRange.pos, /*prefixSpace*/ !!(format & ListFormat.SpaceBetweenSiblings), /*forceNoNewline*/ true); } writeLine(separatingLineTerminatorCount); @@ -4792,13 +4792,13 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } } - // Emit this child. - if (shouldEmitInterveningComments) { - const commentRange = getCommentRange(child); - emitTrailingCommentsOfPosition(commentRange.pos); - } - else { - shouldEmitInterveningComments = mayEmitInterveningComments; + // Emit this child. + if (shouldEmitInterveningComments) { + const commentRange = getCommentRange(child); + emitTrailingCommentsOfPosition(commentRange.pos); + } + else { + shouldEmitInterveningComments = mayEmitInterveningComments; } nextListElementPos = child.pos; @@ -6108,26 +6108,27 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } } - function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { - // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments - - // Check if this position is within a JSX element that contains comment-only text children - // If so, skip emission as the JSX processor will handle these comments - if (currentJsxElement && end >= currentJsxElement.pos && end <= currentJsxElement.end) { - // Check if any of the JSX children are comment-only text nodes - const hasCommentOnlyText = currentJsxElement.children.some(child => - child.kind === SyntaxKind.JsxText && - (child as JsxText).text.trim().startsWith('/*') && - (child as JsxText).text.trim().endsWith('*/') - ); - if (hasCommentOnlyText) { - return; // Skip comment emission - will be handled by JSX processing - } - } - - if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { - forEachTrailingCommentRange(currentSourceFile.text, end, cb); - } + function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { + // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments + + // Check if this position is within a JSX element that contains comment-only text children + // If so, skip emission as the JSX processor will handle these comments + if (currentJsxElement && end >= currentJsxElement.pos && end <= currentJsxElement.end) { + // Check if any of the JSX children are comment-only text nodes + const hasCommentOnlyText = currentJsxElement.children.some(child => { + if (child.kind === SyntaxKind.JsxText) { + return child.text.trim().startsWith("/*") && child.text.trim().endsWith("*/"); + } + return false; + }); + if (hasCommentOnlyText) { + return; // Skip comment emission - will be handled by JSX processing + } + } + + if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { + forEachTrailingCommentRange(currentSourceFile.text, end, cb); + } } function hasDetachedComments(pos: number) { diff --git a/tests/baselines/reference/jsxCommentDuplication.js b/tests/baselines/reference/jsxCommentDuplication.js new file mode 100644 index 0000000000000..42cf5304a7a95 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication.js @@ -0,0 +1,9 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +//// [jsxCommentDuplication.tsx] +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + +//// [jsxCommentDuplication.jsx] +function App() { } +var jsx = /* no */{123}/* no */; diff --git a/tests/baselines/reference/jsxCommentDuplication.symbols b/tests/baselines/reference/jsxCommentDuplication.symbols new file mode 100644 index 0000000000000..328bd5542845d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication.symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplication.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplication.types b/tests/baselines/reference/jsxCommentDuplication.types new file mode 100644 index 0000000000000..5a4fbfe5078c4 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication.types @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : error +>/* no */{/* 1 */ 123 /* 2 */}/* no */ : error +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/baselines/reference/jsxCommentDuplicationDebug.js b/tests/baselines/reference/jsxCommentDuplicationDebug.js new file mode 100644 index 0000000000000..cd1898a0501c6 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplicationDebug.js @@ -0,0 +1,9 @@ +//// [tests/cases/compiler/jsxCommentDuplicationDebug.tsx] //// + +//// [jsxCommentDuplicationDebug.tsx] +function App() {} +const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; + +//// [jsxCommentDuplicationDebug.jsx] +function App() { } +var jsx = /* before */{123}/* after */; diff --git a/tests/baselines/reference/jsxCommentDuplicationDebug.symbols b/tests/baselines/reference/jsxCommentDuplicationDebug.symbols new file mode 100644 index 0000000000000..a5801dee31379 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplicationDebug.symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplicationDebug.tsx] //// + +=== jsxCommentDuplicationDebug.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplicationDebug.tsx, 0, 0)) + +const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplicationDebug.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplicationDebug.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplicationDebug.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplicationDebug.types b/tests/baselines/reference/jsxCommentDuplicationDebug.types new file mode 100644 index 0000000000000..84f6d3b529b0f --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplicationDebug.types @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/jsxCommentDuplicationDebug.tsx] //// + +=== jsxCommentDuplicationDebug.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; +>jsx : error +>/* before */{/* 1 */ 123 /* 2 */}/* after */ : error +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/cases/compiler/jsxCommentDuplicationDebug.tsx b/tests/cases/compiler/jsxCommentDuplicationDebug.tsx deleted file mode 100644 index e18a78c5279bb..0000000000000 --- a/tests/cases/compiler/jsxCommentDuplicationDebug.tsx +++ /dev/null @@ -1,3 +0,0 @@ -// @jsx: preserve -function App() {} -const jsx = /* before */{/* 1 */ 123 /* 2 */}/* after */; \ No newline at end of file From 0cb049ffc7e0b1231cebfbb9e1f29706003bbde4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 07:23:21 +0000 Subject: [PATCH 08/10] Fix JSX comment duplication by excluding JSX text ranges from comment emission Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 58 +++++++++++-------- .../reference/jsxCommentDuplication.js | 2 +- .../reference/tsxAttributeResolution14.js | 9 +-- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ec7e918daa048..dee6758ef1559 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1249,12 +1249,12 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri var containerPos = -1; var containerEnd = -1; var declarationListContainerEnd = -1; - // Track JSX elements to prevent duplicate comment emission - var currentJsxElement: JsxElement | undefined; var currentLineMap: readonly number[] | undefined; var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; var hasWrittenComment = false; var commentsDisabled = !!printerOptions.removeComments; + // Track JSX text ranges to prevent them from being treated as comments + var jsxTextRanges: { start: number; end: number; }[] = []; var lastSubstitution: Node | undefined; var currentParenthesizerRule: ParenthesizerRule | undefined; var { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment"); @@ -1389,11 +1389,22 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri currentSourceFile = sourceFile; currentLineMap = undefined; detachedCommentsInfo = undefined; + jsxTextRanges = []; // Clear JSX text ranges for new source file + + // Pre-collect all JSX text ranges before emission starts if (sourceFile) { + collectJsxTextRanges(sourceFile); setSourceMapSource(sourceFile); } } + function collectJsxTextRanges(node: Node) { + if (node.kind === SyntaxKind.JsxText) { + jsxTextRanges.push({ start: node.pos, end: node.end }); + } + forEachChild(node, collectJsxTextRanges); + } + function setWriter(_writer: EmitTextWriter | undefined, _sourceMapGenerator: SourceMapGenerator | undefined) { if (_writer && printerOptions.omitTrailingSemicolon) { _writer = getTrailingSemicolonDeferringWriter(_writer); @@ -3860,14 +3871,9 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // function emitJsxElement(node: JsxElement) { - const savedJsxElement = currentJsxElement; - currentJsxElement = node; - emit(node.openingElement); emitList(node, node.children, ListFormat.JsxElementOrFragmentChildren); emit(node.closingElement); - - currentJsxElement = savedJsxElement; } function emitJsxSelfClosingElement(node: JsxSelfClosingElement) { @@ -6103,31 +6109,27 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri forEachLeadingCommentWithoutDetachedComments(cb); } else { - forEachLeadingCommentRange(currentSourceFile.text, pos, cb, /*state*/ pos); + forEachLeadingCommentRange(currentSourceFile.text, pos, (commentPos, commentEnd, kind, hasTrailingNewLine, rangePos) => { + // Check if this comment position falls within any JSX text range + const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); + if (!isWithinJsxText) { + cb(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); + } + }, /*state*/ pos); } } } function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) { // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments - - // Check if this position is within a JSX element that contains comment-only text children - // If so, skip emission as the JSX processor will handle these comments - if (currentJsxElement && end >= currentJsxElement.pos && end <= currentJsxElement.end) { - // Check if any of the JSX children are comment-only text nodes - const hasCommentOnlyText = currentJsxElement.children.some(child => { - if (child.kind === SyntaxKind.JsxText) { - return child.text.trim().startsWith("/*") && child.text.trim().endsWith("*/"); + if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { + forEachTrailingCommentRange(currentSourceFile.text, end, (commentPos, commentEnd, kind, hasTrailingNewLine) => { + // Check if this comment position falls within any JSX text range + const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); + if (!isWithinJsxText) { + cb(commentPos, commentEnd, kind, hasTrailingNewLine); } - return false; }); - if (hasCommentOnlyText) { - return; // Skip comment emission - will be handled by JSX processing - } - } - - if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { - forEachTrailingCommentRange(currentSourceFile.text, end, cb); } } @@ -6146,7 +6148,13 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri detachedCommentsInfo = undefined; } - forEachLeadingCommentRange(currentSourceFile.text, pos, cb, /*state*/ pos); + forEachLeadingCommentRange(currentSourceFile.text, pos, (commentPos, commentEnd, kind, hasTrailingNewLine, rangePos) => { + // Check if this comment position falls within any JSX text range + const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); + if (!isWithinJsxText) { + cb(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); + } + }, /*state*/ pos); } function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) { diff --git a/tests/baselines/reference/jsxCommentDuplication.js b/tests/baselines/reference/jsxCommentDuplication.js index 42cf5304a7a95..15426054ad301 100644 --- a/tests/baselines/reference/jsxCommentDuplication.js +++ b/tests/baselines/reference/jsxCommentDuplication.js @@ -6,4 +6,4 @@ const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; //// [jsxCommentDuplication.jsx] function App() { } -var jsx = /* no */{123}/* no */; +var jsx = /* no */{/* 1 */123 /* 2 */}/* no */; diff --git a/tests/baselines/reference/tsxAttributeResolution14.js b/tests/baselines/reference/tsxAttributeResolution14.js index 26633239d3918..1d20896a73dd7 100644 --- a/tests/baselines/reference/tsxAttributeResolution14.js +++ b/tests/baselines/reference/tsxAttributeResolution14.js @@ -35,11 +35,8 @@ function VerticalNavMenuItem(prop) { } function VerticalNav() { return (
- // error - // error - // ok - // ok - // error - // error + // error + // ok + // error
); } From 381423f459117fdd67b6fec0aa334a32246da81f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 07:35:59 +0000 Subject: [PATCH 09/10] Add question about JSX comment handling to documentation Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- .github/copilot-questions.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/copilot-questions.md b/.github/copilot-questions.md index 250d4bf2d776d..d9c9c74a0afa8 100644 --- a/.github/copilot-questions.md +++ b/.github/copilot-questions.md @@ -1,4 +1,5 @@ -Questions I have that I think the developers of this project can help me with: - * How does control flow analysis represent a circular graph? I checked the documentation server for "cfa" and "control flow" - * How do I tell if a symbol is in the global scope? I checked the documentation server for topics referencing "symbol" and "global" - * What is an `EscapedName`, exactly? +Questions I have that I think the developers of this project can help me with: + * How does control flow analysis represent a circular graph? I checked the documentation server for "cfa" and "control flow" + * How do I tell if a symbol is in the global scope? I checked the documentation server for topics referencing "symbol" and "global" + * What is an `EscapedName`, exactly? + * How does TypeScript distinguish between comments inside JSX text vs regular TypeScript comments? I searched for "JSX text comments emitter forEachTrailingCommentRange JSX text ranges" - The issue was that comment iteration doesn't understand JSX context and treats JSX text content as regular comments. From 93c7172440813d8050b01f3ff583dd4067b79fd2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Jun 2025 16:49:35 +0000 Subject: [PATCH 10/10] Improve JSX comment duplication fix - use targeted node traversal instead of expensive pre-walk Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- src/compiler/emitter.ts | 63 ++++++++++++------- .../jsxCommentDuplication(jsx=preserve).js | 9 +++ ...sxCommentDuplication(jsx=preserve).symbols | 11 ++++ .../jsxCommentDuplication(jsx=preserve).types | 17 +++++ ...sxCommentDuplication(jsx=react).errors.txt | 8 +++ .../jsxCommentDuplication(jsx=react).js | 12 ++++ .../jsxCommentDuplication(jsx=react).symbols | 11 ++++ .../jsxCommentDuplication(jsx=react).types | 19 ++++++ ...mmentDuplication(jsx=react-jsx).errors.txt | 8 +++ .../jsxCommentDuplication(jsx=react-jsx).js | 12 ++++ ...xCommentDuplication(jsx=react-jsx).symbols | 11 ++++ ...jsxCommentDuplication(jsx=react-jsx).types | 19 ++++++ .../cases/compiler/jsxCommentDuplication.tsx | 2 +- 13 files changed, 180 insertions(+), 22 deletions(-) create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=preserve).js create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=preserve).symbols create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=preserve).types create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react).errors.txt create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react).js create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react).symbols create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react).types create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).errors.txt create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).js create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).symbols create mode 100644 tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).types diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index dee6758ef1559..e6c55e11e45d8 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1253,8 +1253,7 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri var detachedCommentsInfo: { nodePos: number; detachedCommentEndPos: number; }[] | undefined; var hasWrittenComment = false; var commentsDisabled = !!printerOptions.removeComments; - // Track JSX text ranges to prevent them from being treated as comments - var jsxTextRanges: { start: number; end: number; }[] = []; + var jsxTextPositionCache: Map | undefined; var lastSubstitution: Node | undefined; var currentParenthesizerRule: ParenthesizerRule | undefined; var { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment"); @@ -1389,22 +1388,13 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri currentSourceFile = sourceFile; currentLineMap = undefined; detachedCommentsInfo = undefined; - jsxTextRanges = []; // Clear JSX text ranges for new source file + jsxTextPositionCache = undefined; // Clear cache for new source file - // Pre-collect all JSX text ranges before emission starts if (sourceFile) { - collectJsxTextRanges(sourceFile); setSourceMapSource(sourceFile); } } - function collectJsxTextRanges(node: Node) { - if (node.kind === SyntaxKind.JsxText) { - jsxTextRanges.push({ start: node.pos, end: node.end }); - } - forEachChild(node, collectJsxTextRanges); - } - function setWriter(_writer: EmitTextWriter | undefined, _sourceMapGenerator: SourceMapGenerator | undefined) { if (_writer && printerOptions.omitTrailingSemicolon) { _writer = getTrailingSemicolonDeferringWriter(_writer); @@ -1415,6 +1405,40 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri sourceMapsDisabled = !writer || !sourceMapGenerator; } + function isPositionInJsxText(pos: number): boolean { + if (!currentSourceFile) return false; + + // Use cache if available + if (!jsxTextPositionCache) { + jsxTextPositionCache = new Map(); + } + + if (jsxTextPositionCache.has(pos)) { + return jsxTextPositionCache.get(pos)!; + } + + // Walk the AST to find if this position is within a JSX text node + let found = false; + + function visitNode(node: Node): void { + if (found) return; + + if (node.kind === SyntaxKind.JsxText && pos >= node.pos && pos < node.end) { + found = true; + return; + } + + // Only continue traversing if this node might contain the position + if (pos >= node.pos && pos < node.end) { + forEachChild(node, visitNode); + } + } + + visitNode(currentSourceFile); + jsxTextPositionCache.set(pos, found); + return found; + } + function reset() { nodeIdToGeneratedName = []; nodeIdToGeneratedPrivateName = []; @@ -6110,9 +6134,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } else { forEachLeadingCommentRange(currentSourceFile.text, pos, (commentPos, commentEnd, kind, hasTrailingNewLine, rangePos) => { - // Check if this comment position falls within any JSX text range - const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); - if (!isWithinJsxText) { + // Skip comments that are within JSX text nodes + if (!isPositionInJsxText(commentPos)) { cb(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); } }, /*state*/ pos); @@ -6124,9 +6147,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments if (currentSourceFile && (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd))) { forEachTrailingCommentRange(currentSourceFile.text, end, (commentPos, commentEnd, kind, hasTrailingNewLine) => { - // Check if this comment position falls within any JSX text range - const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); - if (!isWithinJsxText) { + // Skip comments that are within JSX text nodes + if (!isPositionInJsxText(commentPos)) { cb(commentPos, commentEnd, kind, hasTrailingNewLine); } }); @@ -6149,9 +6171,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri } forEachLeadingCommentRange(currentSourceFile.text, pos, (commentPos, commentEnd, kind, hasTrailingNewLine, rangePos) => { - // Check if this comment position falls within any JSX text range - const isWithinJsxText = jsxTextRanges.some(range => commentPos >= range.start && commentEnd <= range.end); - if (!isWithinJsxText) { + // Skip comments that are within JSX text nodes + if (!isPositionInJsxText(commentPos)) { cb(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos); } }, /*state*/ pos); diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).js b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).js new file mode 100644 index 0000000000000..15426054ad301 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).js @@ -0,0 +1,9 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +//// [jsxCommentDuplication.tsx] +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + +//// [jsxCommentDuplication.jsx] +function App() { } +var jsx = /* no */{/* 1 */123 /* 2 */}/* no */; diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).symbols b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).symbols new file mode 100644 index 0000000000000..328bd5542845d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplication.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).types b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).types new file mode 100644 index 0000000000000..5a4fbfe5078c4 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=preserve).types @@ -0,0 +1,17 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : error +>/* no */{/* 1 */ 123 /* 2 */}/* no */ : error +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react).errors.txt b/tests/baselines/reference/jsxCommentDuplication(jsx=react).errors.txt new file mode 100644 index 0000000000000..5902bb8f12f9e --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react).errors.txt @@ -0,0 +1,8 @@ +jsxCommentDuplication.tsx(2,14): error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found. + + +==== jsxCommentDuplication.tsx (1 errors) ==== + function App() {} + const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + ~~~ +!!! error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found. \ No newline at end of file diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react).js b/tests/baselines/reference/jsxCommentDuplication(jsx=react).js new file mode 100644 index 0000000000000..37cba9d576718 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react).js @@ -0,0 +1,12 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +//// [jsxCommentDuplication.tsx] +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + +//// [jsxCommentDuplication.js] +function App() { } +var jsx = React.createElement(App, null, + "/* no */", /* 1 */ + 123 /* 2 */, + "/* no */"); diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react).symbols b/tests/baselines/reference/jsxCommentDuplication(jsx=react).symbols new file mode 100644 index 0000000000000..328bd5542845d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react).symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplication.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react).types b/tests/baselines/reference/jsxCommentDuplication(jsx=react).types new file mode 100644 index 0000000000000..d0333134fdd1c --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react).types @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : any +> : ^^^ +>/* no */{/* 1 */ 123 /* 2 */}/* no */ : any +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).errors.txt b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).errors.txt new file mode 100644 index 0000000000000..a821a45616d2d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).errors.txt @@ -0,0 +1,8 @@ +jsxCommentDuplication.tsx(2,13): error TS2875: This JSX tag requires the module path 'react/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. + + +==== jsxCommentDuplication.tsx (1 errors) ==== + function App() {} + const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2875: This JSX tag requires the module path 'react/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. \ No newline at end of file diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).js b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).js new file mode 100644 index 0000000000000..b28abc2eb3933 --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).js @@ -0,0 +1,12 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +//// [jsxCommentDuplication.tsx] +function App() {} +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; + +//// [jsxCommentDuplication.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var jsx_runtime_1 = require("react/jsx-runtime"); +function App() { } +var jsx = (0, jsx_runtime_1.jsxs)(App, { children: ["/* no */", /* 1 */ 123 /* 2 */, "/* no */"] }); diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).symbols b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).symbols new file mode 100644 index 0000000000000..328bd5542845d --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).symbols @@ -0,0 +1,11 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : Symbol(jsx, Decl(jsxCommentDuplication.tsx, 1, 5)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) +>App : Symbol(App, Decl(jsxCommentDuplication.tsx, 0, 0)) + diff --git a/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).types b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).types new file mode 100644 index 0000000000000..d0333134fdd1c --- /dev/null +++ b/tests/baselines/reference/jsxCommentDuplication(jsx=react-jsx).types @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/jsxCommentDuplication.tsx] //// + +=== jsxCommentDuplication.tsx === +function App() {} +>App : () => void +> : ^^^^^^^^^^ + +const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; +>jsx : any +> : ^^^ +>/* no */{/* 1 */ 123 /* 2 */}/* no */ : any +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ +>123 : 123 +> : ^^^ +>App : () => void +> : ^^^^^^^^^^ + diff --git a/tests/cases/compiler/jsxCommentDuplication.tsx b/tests/cases/compiler/jsxCommentDuplication.tsx index fe13875536038..a46de1b2ac76b 100644 --- a/tests/cases/compiler/jsxCommentDuplication.tsx +++ b/tests/cases/compiler/jsxCommentDuplication.tsx @@ -1,3 +1,3 @@ -// @jsx: preserve +// @jsx: preserve,react,react-jsx function App() {} const jsx = /* no */{/* 1 */ 123 /* 2 */}/* no */; \ No newline at end of file