Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions _extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
{
"title": "TypeScript Native Preview",
"properties": {
"typescript.native-preview.customConfigFileName": {
"type": "string",
"description": "Custom config file name to use, before defaulting to tsconfig.json/jsconfig.json.",
"tags": ["experimental"]
},
"typescript.native-preview.trace.server": {
"type": "string",
"enum": [
Expand Down
20 changes: 13 additions & 7 deletions _extension/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,11 @@ export class Client {
this.exe = exe;
this.outputChannel.appendLine(`Resolved to ${this.exe.path}`);

// Get pprofDir
// Get pprofDir
Copy link
Member

@DanielRosenwasser DanielRosenwasser Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switch this and every other TS line back to spaces

const config = vscode.workspace.getConfiguration("typescript.native-preview");
const pprofDir = config.get<string>("pprofDir");
const pprofArgs = pprofDir ? ["--pprofDir", pprofDir] : [];
const customConfigFileName = config.get<string>("customConfigFileName") ?? "";

const serverOptions: ServerOptions = {
run: {
Expand All @@ -108,12 +109,17 @@ export class Client {
},
};

this.client = new LanguageClient(
"typescript.native-preview",
"typescript.native-preview-lsp",
serverOptions,
this.clientOptions,
);
this.client = new LanguageClient(
"typescript.native-preview",
"typescript.native-preview-lsp",
serverOptions,
{
...this.clientOptions,
initializationOptions: {
customConfigFileName,
Copy link
Member

@DanielRosenwasser DanielRosenwasser Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than be part of initialization, we think this sort of thing would belong more on user preferences (see here.

Whether we just say "you need to restart the server" or we track this sort of thing on the server in some way is a bigger question.

},
},
);

this.outputChannel.appendLine(`Starting language server...`);
await this.client.start();
Expand Down
9 changes: 9 additions & 0 deletions _extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ export async function activate(context: vscode.ExtensionContext) {
}
}
}
if (event.affectsConfiguration("typescript.native-preview.customConfigFileName")) {
// Show prompt to restart to pick up the new initialization options
setTimeout(async () => {
const selected = await vscode.window.showInformationMessage("TypeScript Native Preview settings have changed. Restart extensions to apply changes.", "Restart Extensions");
if (selected) {
vscode.commands.executeCommand("workbench.action.restartExtensionHost");
}
}, 100);
}
}));

const useTsgo = vscode.workspace.getConfiguration("typescript").get<boolean>("experimental.useTsgo");
Expand Down
2 changes: 1 addition & 1 deletion _submodules/TypeScript
Submodule TypeScript updated 6209 files
25 changes: 18 additions & 7 deletions internal/lsp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,15 +701,26 @@ func (s *Server) handleInitialized(ctx context.Context, params *lsproto.Initiali
cwd = s.cwd
}

// Read customConfigFileName from initializationOptions if provided
var customConfigFileName string
if s.initializeParams != nil && s.initializeParams.InitializationOptions != nil && *s.initializeParams.InitializationOptions != nil {
if m, ok := (*s.initializeParams.InitializationOptions).(map[string]any); ok {
if v, ok := m["customConfigFileName"].(string); ok {
customConfigFileName = v
}
}
}

s.session = project.NewSession(&project.SessionInit{
Options: &project.SessionOptions{
CurrentDirectory: cwd,
DefaultLibraryPath: s.defaultLibraryPath,
TypingsLocation: s.typingsLocation,
PositionEncoding: s.positionEncoding,
WatchEnabled: s.watchEnabled,
LoggingEnabled: true,
DebounceDelay: 500 * time.Millisecond,
CurrentDirectory: cwd,
DefaultLibraryPath: s.defaultLibraryPath,
TypingsLocation: s.typingsLocation,
PositionEncoding: s.positionEncoding,
WatchEnabled: s.watchEnabled,
LoggingEnabled: true,
DebounceDelay: 500 * time.Millisecond,
CustomConfigFileName: customConfigFileName,
},
FS: s.fs,
Logger: s.logger,
Expand Down
24 changes: 21 additions & 3 deletions internal/project/configfileregistrybuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,19 +495,37 @@ func (c *configFileRegistryBuilder) handleConfigChange(entry *dirty.SyncMapEntry

func (c *configFileRegistryBuilder) computeConfigFileName(fileName string, skipSearchInDirectoryOfFile bool, logger *logging.LogTree) string {
searchPath := tspath.GetDirectoryPath(fileName)
// Prefer custom config file if provided; search ancestors with correct skip behavior.
if c.sessionOptions.CustomConfigFileName != "" {
skipCustom := skipSearchInDirectoryOfFile
if result, _ := tspath.ForEachAncestorDirectory(searchPath, func(directory string) (result string, stop bool) {
customConfigFilePath := tspath.CombinePaths(directory, c.sessionOptions.CustomConfigFileName)
if !skipCustom && c.FS().FileExists(customConfigFilePath) {
return customConfigFilePath, true
}
skipCustom = false
return "", false
}); result != "" {
logger.Logf("computeConfigFileName:: File: %s:: Result: %s", fileName, result)
return result
}
}

// Fallback to tsconfig.json/jsconfig.json
skip := skipSearchInDirectoryOfFile
result, _ := tspath.ForEachAncestorDirectory(searchPath, func(directory string) (result string, stop bool) {
tsconfigPath := tspath.CombinePaths(directory, "tsconfig.json")
if !skipSearchInDirectoryOfFile && c.FS().FileExists(tsconfigPath) {
if !skip && c.FS().FileExists(tsconfigPath) {
return tsconfigPath, true
}
jsconfigPath := tspath.CombinePaths(directory, "jsconfig.json")
if !skipSearchInDirectoryOfFile && c.FS().FileExists(jsconfigPath) {
if !skip && c.FS().FileExists(jsconfigPath) {
return jsconfigPath, true
}
if strings.HasSuffix(directory, "/node_modules") {
return "", true
}
skipSearchInDirectoryOfFile = false
skip = false
return "", false
})
logger.Logf("computeConfigFileName:: File: %s:: Result: %s", fileName, result)
Expand Down
15 changes: 8 additions & 7 deletions internal/project/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ const (
// SessionOptions are the immutable initialization options for a session.
// Snapshots may reference them as a pointer since they never change.
type SessionOptions struct {
CurrentDirectory string
DefaultLibraryPath string
TypingsLocation string
PositionEncoding lsproto.PositionEncodingKind
WatchEnabled bool
LoggingEnabled bool
DebounceDelay time.Duration
CurrentDirectory string
DefaultLibraryPath string
TypingsLocation string
PositionEncoding lsproto.PositionEncodingKind
WatchEnabled bool
LoggingEnabled bool
DebounceDelay time.Duration
CustomConfigFileName string
}

type SessionInit struct {
Expand Down
Loading