Skip to content

Commit 82fa46c

Browse files
authored
Merge branch 'main' into gabritto/issue3676pt2
2 parents 8863358 + e5a356e commit 82fa46c

341 files changed

Lines changed: 10692 additions & 676 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ This is still a work in progress and is not yet at full feature parity with Type
3232
| Commandline and `tsconfig.json` parsing | done | Done, though `tsconfig` errors may not be as helpful. |
3333
| Type resolution | done | Same types as TS 6.0. |
3434
| Type checking | done | Same errors, locations, and messages as TS 6.0. Types printback in errors may display differently. |
35-
| JavaScript-specific inference and JSDoc | in progress | Mostly complete, but intentionally lacking some features. Declaration emit not complete. |
35+
| JavaScript-specific inference and JSDoc | done | Complete, but intentionally lacking some features. Declaration emit differs greatly, intentionally, to be closer to TS declarations. |
3636
| JSX | done | - |
37-
| Declaration emit | in progress | Done for TypeScript files. Not yet complete for JavaScript files. |
37+
| Declaration emit | done | - |
3838
| Emit (JS output) | done | - |
3939
| Watch mode | prototype | Watches files and rebuilds, but no incremental rechecking. Not optimized. |
4040
| Build mode / project references | done | - |

_extension/src/client.ts

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export class Client implements vscode.Disposable {
4949
isInitialized = false;
5050

5151
private exe: ExeInfo | undefined;
52+
private errorHandler: ReportingErrorHandler;
5253

5354
constructor(
5455
outputChannel: vscode.LogOutputChannel,
@@ -60,6 +61,17 @@ export class Client implements vscode.Disposable {
6061
this.traceOutputChannel = traceOutputChannel;
6162
this.initializedEventEmitter = initializedEventEmitter;
6263
this.telemetryReporter = telemetryReporter;
64+
this.errorHandler = new ReportingErrorHandler(this.telemetryReporter, 5);
65+
66+
// Monkey-patch the output channel's error method to capture recent stderr lines.
67+
// When the server crashes, vscode-languageclient pipes stderr to outputChannel.error(),
68+
// so the error handler can include the last N lines in crash telemetry.
69+
const originalError = this.outputChannel.error.bind(this.outputChannel);
70+
this.outputChannel.error = (...args: Parameters<typeof this.outputChannel.error>) => {
71+
originalError(...args);
72+
this.errorHandler.pushStderrLine(String(args[0]));
73+
};
74+
6375
this.documentSelector = [
6476
...jsTsLanguageModes.map(language => ({ scheme: "file", language })),
6577
...jsTsLanguageModes.map(language => ({ scheme: "untitled", language })),
@@ -72,7 +84,7 @@ export class Client implements vscode.Disposable {
7284
codeLensShowLocationsCommandName,
7385
enableTelemetry: true,
7486
},
75-
errorHandler: new ReportingErrorHandler(this.telemetryReporter, 5),
87+
errorHandler: this.errorHandler,
7688
middleware: {
7789
workspace: {
7890
...configurationMiddleware,
@@ -345,13 +357,45 @@ class ReportingErrorHandler implements ErrorHandler {
345357
telemetryReporter: tr.TelemetryReporter;
346358
maxRestartCount: number;
347359
restarts: number[];
360+
private stderrBuffer: string[] = [];
361+
private capturingPanic = false;
362+
private static readonly maxStderrLines = 40;
363+
private static readonly maxStderrLength = 8192;
348364

349365
constructor(telemetryReporter: tr.TelemetryReporter, maxRestartCount: number) {
350366
this.telemetryReporter = telemetryReporter;
351367
this.maxRestartCount = maxRestartCount;
352368
this.restarts = [];
353369
}
354370

371+
pushStderrLine(line: string): void {
372+
for (const l of line.split("\n")) {
373+
if (!this.capturingPanic) {
374+
if (/^panic:/.test(l.trimStart())) {
375+
// Clear any stale data from a previous session/panic.
376+
this.stderrBuffer = [];
377+
this.capturingPanic = true;
378+
}
379+
else {
380+
continue;
381+
}
382+
}
383+
if (this.stderrBuffer.length < ReportingErrorHandler.maxStderrLines) {
384+
this.stderrBuffer.push(l);
385+
}
386+
else {
387+
this.capturingPanic = false;
388+
}
389+
}
390+
}
391+
392+
private consumeStderrBuffer(): string {
393+
const raw = this.stderrBuffer.join("\n");
394+
this.stderrBuffer = [];
395+
this.capturingPanic = false;
396+
return sanitizeStderr(raw).slice(0, ReportingErrorHandler.maxStderrLength);
397+
}
398+
355399
error(_error: Error, _message: Message | undefined, count: number | undefined): ErrorHandlerResult | Promise<ErrorHandlerResult> {
356400
let errorAction = ErrorAction.Shutdown;
357401
if (count && count <= 3) {
@@ -405,8 +449,10 @@ class ReportingErrorHandler implements ErrorHandler {
405449
default:
406450
const _: never = resultingAction;
407451
}
452+
const lastStderr = this.consumeStderrBuffer();
408453
this.telemetryReporter.sendTelemetryErrorEvent("languageServer.connectionClosed", {
409454
resultingAction: actionString,
455+
lastStderr,
410456
});
411457

412458
if (resultingAction === CloseAction.DoNotRestart) {
@@ -419,3 +465,59 @@ class ReportingErrorHandler implements ErrorHandler {
419465
return { action: resultingAction };
420466
}
421467
}
468+
469+
// Matches the server-side sanitizeStackTrace in internal/lsp/stack_sanitizer.go.
470+
// Strips file path prefixes that may contain PII and redacts frames outside of our module.
471+
const genericSecretKeywordRegex = /\b(key|token|signature|sig|pwd)([(\[.|])/gi;
472+
473+
function sanitizeStderr(stderr: string): string {
474+
if (!stderr) {
475+
return "";
476+
}
477+
return stderr.split("\n").map(sanitizeStderrLine).join("\n");
478+
}
479+
480+
function sanitizeStderrLine(line: string): string {
481+
// Keep "goroutine N [status]:" headers as-is.
482+
if (/^goroutine \d+/.test(line)) {
483+
return line;
484+
}
485+
// Redact the panic message itself — assert messages may contain user data.
486+
// Keep only "panic:" as a marker.
487+
if (/^panic:/.test(line.trimStart())) {
488+
return "panic: (REDACTED)";
489+
}
490+
// Keep "Server process exited" messages from vscode-languageclient.
491+
if (line.includes("Server process exited")) {
492+
return line;
493+
}
494+
495+
const leadingWhitespace = line.match(/^(\s*)/)?.[1] ?? "";
496+
497+
// Stack frame file path lines look like: \t/full/path/to/file.go:123 +0x40
498+
// Function lines look like: github.com/microsoft/typescript-go/internal/foo.Bar(...)
499+
const ourModuleMarker = "typescript-go/internal";
500+
const idx = line.indexOf(ourModuleMarker);
501+
if (idx >= 0) {
502+
let relevantPart = line.slice(idx);
503+
// Strip hex offset suffixes like " +0x40"
504+
relevantPart = relevantPart.replace(/ \+0x[0-9a-fA-F]+$/, "");
505+
// Strip " in goroutine N" suffixes
506+
relevantPart = relevantPart.replace(/ in goroutine \d+$/, "");
507+
// Strip function arguments (keep parens empty)
508+
relevantPart = relevantPart.replace(/\([^)]*\)$/, "()");
509+
// Replace / with |> to defeat path-based secret detection
510+
relevantPart = relevantPart.replace(/\//g, "|>");
511+
// Defeat generic secret keyword regex
512+
relevantPart = relevantPart.replace(genericSecretKeywordRegex, "$1X_X$2");
513+
return leadingWhitespace + relevantPart;
514+
}
515+
516+
// Preserve completely blank lines.
517+
if (line.trim() === "") {
518+
return "";
519+
}
520+
521+
// Non-internal frames get fully redacted.
522+
return leadingWhitespace + "(REDACTED)";
523+
}

_extension/src/telemetryReporting.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export type LSConnectionError = {
8181

8282
export type LSServerConnectionClosed = {
8383
resultingAction: string;
84+
lastStderr: string;
8485
};
8586

8687
export type LSErrorResponse = {

_extension/src/util.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ export async function restartExtHostOnChangeIfNeeded(): Promise<void> {
147147
* `typescript.experimental.useTsgo`, using `inspect()` to only consider
148148
* explicitly set values (ignoring VS Code defaults).
149149
*
150-
* Returns `true` if either setting is explicitly `true`, `false` if either
151-
* is explicitly `false` (and neither is `true`), or `undefined` if neither
152-
* setting has been explicitly configured.
150+
* Each setting key is resolved using standard VS Code precedence (workspace
151+
* folder > workspace > global, with language-specific overrides taking
152+
* priority within each scope). Returns `true` if either resolved value is
153+
* `true`, `false` if either is `false` (and neither is `true`), or
154+
* `undefined` if neither setting has been explicitly configured.
153155
*/
154156
export function getUseTsgo(): boolean | undefined {
155157
const tsValue = getExplicitUseTsgo("typescript");
@@ -184,8 +186,9 @@ function getExplicitUseTsgo(section: string): boolean | undefined {
184186
inspected.globalValue,
185187
];
186188

187-
if (explicitValues.some(v => v === true)) return true;
188-
if (explicitValues.some(v => v === false)) return false;
189+
for (const v of explicitValues) {
190+
if (v !== undefined) return v;
191+
}
189192
return undefined;
190193
}
191194

_packages/native-preview/package.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,47 +40,47 @@
4040
},
4141
"exports": {
4242
"./package.json": "./package.json",
43-
"./sync": {
43+
"./unstable/sync": {
4444
"@typescript/source": "./src/api/sync/api.ts",
4545
"default": "./dist/api/sync/api.js"
4646
},
47-
"./async": {
47+
"./unstable/async": {
4848
"@typescript/source": "./src/api/async/api.ts",
4949
"default": "./dist/api/async/api.js"
5050
},
51-
"./fs": {
51+
"./unstable/fs": {
5252
"@typescript/source": "./src/api/fs.ts",
5353
"default": "./dist/api/fs.js"
5454
},
55-
"./proto": {
55+
"./unstable/proto": {
5656
"@typescript/source": "./src/api/proto.ts",
5757
"default": "./dist/api/proto.js"
5858
},
59-
"./ast": {
59+
"./unstable/ast": {
6060
"@typescript/source": "./src/ast/index.ts",
6161
"default": "./dist/ast/index.js"
6262
},
63-
"./ast/is": {
63+
"./unstable/ast/is": {
6464
"@typescript/source": "./src/ast/is.ts",
6565
"default": "./dist/ast/is.js"
6666
},
67-
"./ast/factory": {
67+
"./unstable/ast/factory": {
6868
"@typescript/source": "./src/ast/factory.generated.ts",
6969
"default": "./dist/ast/factory.generated.js"
7070
},
71-
"./ast/utils": {
71+
"./unstable/ast/utils": {
7272
"@typescript/source": "./src/ast/utils.ts",
7373
"default": "./dist/ast/utils.js"
7474
},
75-
"./ast/scanner": {
75+
"./unstable/ast/scanner": {
7676
"@typescript/source": "./src/ast/scanner.ts",
7777
"default": "./dist/ast/scanner.js"
7878
},
79-
"./ast/visitor": {
79+
"./unstable/ast/visitor": {
8080
"@typescript/source": "./src/ast/visitor.ts",
8181
"default": "./dist/ast/visitor.js"
8282
},
83-
"./ast/clone": {
83+
"./unstable/ast/clone": {
8484
"@typescript/source": "./src/ast/clone.ts",
8585
"default": "./dist/ast/clone.js"
8686
}

_packages/native-preview/test/async/api.bench.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import {
22
type Node,
33
type SourceFile,
44
SyntaxKind,
5-
} from "@typescript/native-preview/ast";
5+
} from "@typescript/native-preview/unstable/ast";
66
import {
77
API,
88
type Project,
99
type Snapshot,
10-
} from "@typescript/native-preview/async"; // @sync: } from "@typescript/native-preview/sync";
10+
} from "@typescript/native-preview/unstable/async"; // @sync: } from "@typescript/native-preview/unstable/sync";
1111
import {
1212
existsSync,
1313
writeFileSync,

0 commit comments

Comments
 (0)