From 57bd420d6b2a37dad49f8a682d473830417fda9b Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Wed, 3 Jul 2024 23:11:06 +0100 Subject: [PATCH] feat: "deno.env" and "deno.envFile" settings (#1128) --- client/package-lock.json | 18 +++++++++ client/package.json | 1 + client/src/commands.ts | 56 ++++++++++++++++++++++++---- client/src/debug_config_provider.ts | 58 ++++++++++++++++++----------- client/src/extension.ts | 2 + client/src/upgrade.ts | 28 ++++++++++++-- package.json | 23 ++++++++++++ 7 files changed, 155 insertions(+), 31 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 0475c121..6825e157 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,6 +9,7 @@ "version": "3.37.1", "license": "MIT", "dependencies": { + "dotenv": "^16.4.5", "jsonc-parser": "^3.2.0", "semver": "7.5.2", "vscode-languageclient": "^8.0.1" @@ -155,6 +156,18 @@ } } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -619,6 +632,11 @@ "ms": "2.1.2" } }, + "dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" + }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", diff --git a/client/package.json b/client/package.json index bf0ece78..4b4a739b 100644 --- a/client/package.json +++ b/client/package.json @@ -14,6 +14,7 @@ "vscode": "^1.60.0" }, "dependencies": { + "dotenv": "^16.4.5", "jsonc-parser": "^3.2.0", "semver": "7.5.2", "vscode-languageclient": "^8.0.1" diff --git a/client/src/commands.ts b/client/src/commands.ts index 08bca116..3957520b 100644 --- a/client/src/commands.ts +++ b/client/src/commands.ts @@ -29,12 +29,15 @@ import { registryState } from "./lsp_extensions"; import { createRegistryStateHandler } from "./notification_handlers"; import { DenoServerInfo } from "./server_info"; +import * as dotenv from "dotenv"; import * as semver from "semver"; import * as vscode from "vscode"; import { LanguageClient, ServerOptions } from "vscode-languageclient/node"; import type { Location, Position } from "vscode-languageclient/node"; import { getWorkspacesEnabledInfo, setupCheckConfig } from "./enable"; import { denoUpgradePromptAndExecute } from "./upgrade"; +import { join } from "path"; +import { readFileSync } from "fs"; // deno-lint-ignore no-explicit-any export type Callback = (...args: any[]) => unknown; @@ -118,14 +121,34 @@ export function startLanguageServer( const config = vscode.workspace.getConfiguration(EXTENSION_NS); - const env: Record = { + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + const env: Record = { ...process.env, - "DENO_V8_FLAGS": getV8Flags(), - "NO_COLOR": "1", }; + const denoEnvFile = config.get("envFile"); + if (denoEnvFile) { + if (workspaceFolder) { + const denoEnvPath = join(workspaceFolder.uri.fsPath, denoEnvFile); + try { + const content = readFileSync(denoEnvPath, { encoding: "utf8" }); + const parsed = dotenv.parse(content); + Object.assign(env, parsed); + } catch (error) { + vscode.window.showErrorMessage( + `Could not read env file "${denoEnvPath}": ${error}`, + ); + } + } + } + const denoEnv = config.get>("env"); + if (denoEnv) { + Object.assign(env, denoEnv); + } if (config.get("future")) { env["DENO_FUTURE"] = "1"; } + env["NO_COLOR"] = "1"; + env["DENO_V8_FLAGS"] = getV8Flags(); const serverOptions: ServerOptions = { run: { @@ -349,7 +372,27 @@ export function test( testArgs.push("--import-map", importMap.trim()); } } + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; const env = {} as Record; + const denoEnvFile = config.get("envFile"); + if (denoEnvFile) { + if (workspaceFolder) { + const denoEnvPath = join(workspaceFolder.uri.fsPath, denoEnvFile); + try { + const content = readFileSync(denoEnvPath, { encoding: "utf8" }); + const parsed = dotenv.parse(content); + Object.assign(env, parsed); + } catch (error) { + vscode.window.showErrorMessage( + `Could not read env file "${denoEnvPath}": ${error}`, + ); + } + } + } + const denoEnv = config.get>("env"); + if (denoEnv) { + Object.assign(env, denoEnv); + } const cacheDir: string | undefined | null = config.get("cache"); if (cacheDir?.trim()) { env["DENO_DIR"] = cacheDir.trim(); @@ -367,11 +410,10 @@ export function test( env, }; - assert(vscode.workspace.workspaceFolders); - const target = vscode.workspace.workspaceFolders[0]; + assert(workspaceFolder); const denoCommand = await getDenoCommandName(); const task = tasks.buildDenoTask( - target, + workspaceFolder, denoCommand, definition, `test "${name}"`, @@ -389,7 +431,7 @@ export function test( const createdTask = await vscode.tasks.executeTask(task); if (options?.inspect) { - await vscode.debug.startDebugging(target, { + await vscode.debug.startDebugging(workspaceFolder, { name, request: "attach", type: "node", diff --git a/client/src/debug_config_provider.ts b/client/src/debug_config_provider.ts index 61fdbdd9..c46305ee 100644 --- a/client/src/debug_config_provider.ts +++ b/client/src/debug_config_provider.ts @@ -3,6 +3,7 @@ import * as vscode from "vscode"; import type { DenoExtensionContext } from "./types"; import { getDenoCommandName, getInspectArg } from "./util"; +import { EXTENSION_NS } from "./constants"; export class DenoDebugConfigurationProvider implements vscode.DebugConfigurationProvider { @@ -11,7 +12,12 @@ export class DenoDebugConfigurationProvider #getEnv() { const settings = this.#extensionContext.clientOptions .initializationOptions(); + const config = vscode.workspace.getConfiguration(EXTENSION_NS); const env: Record = {}; + const denoEnv = config.get>("env"); + if (denoEnv) { + Object.assign(env, denoEnv); + } if (settings.cache) { env["DENO_DIR"] = settings.cache; } @@ -48,24 +54,28 @@ export class DenoDebugConfigurationProvider } async provideDebugConfigurations(): Promise { - return [ - { - request: "launch", - name: "Launch Program", - type: "node", - program: "${workspaceFolder}/main.ts", - cwd: "${workspaceFolder}", - env: this.#getEnv(), - runtimeExecutable: await getDenoCommandName(), - runtimeArgs: [ - "run", - ...this.#getAdditionalRuntimeArgs(), - this.#getInspectArg(), - "--allow-all", - ], - attachSimplePort: 9229, - }, - ]; + const config = vscode.workspace.getConfiguration(EXTENSION_NS); + const debugConfig: vscode.DebugConfiguration = { + request: "launch", + name: "Launch Program", + type: "node", + program: "${workspaceFolder}/main.ts", + cwd: "${workspaceFolder}", + env: this.#getEnv(), + runtimeExecutable: await getDenoCommandName(), + runtimeArgs: [ + "run", + ...this.#getAdditionalRuntimeArgs(), + this.#getInspectArg(), + "--allow-all", + ], + attachSimplePort: 9229, + }; + const denoEnvFile = config.get("envFile"); + if (denoEnvFile) { + debugConfig.envFile = denoEnvFile; + } + return [debugConfig]; } async resolveDebugConfiguration( @@ -81,9 +91,9 @@ export class DenoDebugConfigurationProvider (langId === "typescript" || langId === "javascript" || langId === "typescriptreact" || langId === "javascriptreact") ) { - // https://github.com/microsoft/vscode/issues/106703#issuecomment-694595773 // Bypass the bug of the vscode 1.49.0 - vscode.debug.startDebugging(workspace, { + const config = vscode.workspace.getConfiguration(EXTENSION_NS); + const debugConfig: vscode.DebugConfiguration = { request: "launch", name: "Launch Program", type: "node", @@ -97,7 +107,13 @@ export class DenoDebugConfigurationProvider "--allow-all", ], attachSimplePort: 9229, - }); + }; + const denoEnvFile = config.get("envFile"); + if (denoEnvFile) { + debugConfig.envFile = denoEnvFile; + } + // https://github.com/microsoft/vscode/issues/106703#issuecomment-694595773 + vscode.debug.startDebugging(workspace, debugConfig); return undefined; } return null; diff --git a/client/src/extension.ts b/client/src/extension.ts index 2d6f4384..34200db6 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -44,6 +44,8 @@ function handleConfigurationChange(event: vscode.ConfigurationChangeEvent) { event.affectsConfiguration("deno.enable") || event.affectsConfiguration("deno.disablePaths") || event.affectsConfiguration("deno.enablePaths") || + event.affectsConfiguration("deno.env") || + event.affectsConfiguration("deno.envFile") || event.affectsConfiguration("deno.future") || event.affectsConfiguration("deno.internalInspect") || event.affectsConfiguration("deno.logFile") || diff --git a/client/src/upgrade.ts b/client/src/upgrade.ts index e45e4e80..13636ddb 100644 --- a/client/src/upgrade.ts +++ b/client/src/upgrade.ts @@ -1,10 +1,13 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. +import { readFileSync } from "fs"; import { EXTENSION_NS } from "./constants"; import * as tasks from "./tasks"; import { UpgradeAvailable } from "./types"; import { assert, getDenoCommandName } from "./util"; +import * as dotenv from "dotenv"; import * as vscode from "vscode"; +import { join } from "path"; export async function denoUpgradePromptAndExecute( { latestVersion, isCanary }: UpgradeAvailable, @@ -31,7 +34,27 @@ export async function denoUpgradePromptAndExecute( } args.push("--version"); args.push(latestVersion); + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; const env = {} as Record; + const denoEnvFile = config.get("envFile"); + if (denoEnvFile) { + if (workspaceFolder) { + const denoEnvPath = join(workspaceFolder.uri.fsPath, denoEnvFile); + try { + const content = readFileSync(denoEnvPath, { encoding: "utf8" }); + const parsed = dotenv.parse(content); + Object.assign(env, parsed); + } catch (error) { + vscode.window.showErrorMessage( + `Could not read env file "${denoEnvPath}": ${error}`, + ); + } + } + } + const denoEnv = config.get>("env"); + if (denoEnv) { + Object.assign(env, denoEnv); + } const cacheDir: string | undefined | null = config.get("cache"); if (cacheDir?.trim()) { env["DENO_DIR"] = cacheDir.trim(); @@ -45,11 +68,10 @@ export async function denoUpgradePromptAndExecute( args, env, }; - assert(vscode.workspace.workspaceFolders); - const target = vscode.workspace.workspaceFolders[0]; + assert(workspaceFolder); const denoCommand = await getDenoCommandName(); const task = tasks.buildDenoTask( - target, + workspaceFolder, denoCommand, definition, "upgrade", diff --git a/package.json b/package.json index edd39324..0c956fb1 100644 --- a/package.json +++ b/package.json @@ -238,6 +238,29 @@ "C:\\Program Files\\deno\\deno.exe" ] }, + "deno.env": { + "type": "object", + "default": {}, + "patternProperties": { + ".+": { + "type": "string" + } + }, + "markdownDescription": "Additional environment variables to pass to Deno processes. Overrides the user's env and `deno.envFile`. These will be overridden by more specific settings such as `deno.future` for `DENO_FUTURE`, and invariables like `NO_COLOR=1`.", + "scope": "window", + "examples": [ + { "HTTP_PROXY": "http://localhost:8080" } + ] + }, + "deno.envFile": { + "type": "string", + "default": null, + "markdownDescription": "Env file containing additional environment variables to pass to Deno processes. Overrides the user's env. These will be overridden by `deno.env`, more specific settings such as `deno.future` for `DENO_FUTURE`, and invariables like `NO_COLOR=1`.", + "scope": "window", + "examples": [ + ".env" + ] + }, "deno.cache": { "type": "string", "default": null,