From a50027dc0fa7f8cfef48caaada8bf4fdc713f4fa Mon Sep 17 00:00:00 2001 From: rphlmr Date: Wed, 20 Nov 2024 19:28:13 +0100 Subject: [PATCH] vscode plugin rewrite --- vscode-extension/src/context.ts | 66 ++++ vscode-extension/src/extension.ts | 364 ++---------------- .../src/modules/open-visualizer/codelens.ts | 51 +++ .../src/modules/open-visualizer/command.ts | 50 +++ .../src/modules/stop-visualizer/command.ts | 14 + vscode-extension/src/panel.ts | 46 +++ vscode-extension/src/server.ts | 126 ++++++ vscode-extension/src/utils.ts | 38 ++ 8 files changed, 417 insertions(+), 338 deletions(-) create mode 100644 vscode-extension/src/context.ts create mode 100644 vscode-extension/src/modules/open-visualizer/codelens.ts create mode 100644 vscode-extension/src/modules/open-visualizer/command.ts create mode 100644 vscode-extension/src/modules/stop-visualizer/command.ts create mode 100644 vscode-extension/src/panel.ts create mode 100644 vscode-extension/src/server.ts create mode 100644 vscode-extension/src/utils.ts diff --git a/vscode-extension/src/context.ts b/vscode-extension/src/context.ts new file mode 100644 index 0000000..ad2304b --- /dev/null +++ b/vscode-extension/src/context.ts @@ -0,0 +1,66 @@ +import * as vscode from "vscode"; +import { outputChannel } from "./utils"; +import path from "node:path"; + +/* Local state */ +let $context: vscode.ExtensionContext | undefined = undefined; + +/* Constants */ +const OutputKey = "[Context]"; + +export function setExtensionContext(context: vscode.ExtensionContext) { + $context = context; +} + +export function getExtensionContext() { + if (!$context) { + const msg = `${OutputKey} Context not set`; + outputChannel.appendLine(msg); + throw new Error(msg); + } + + return $context; +} + +export async function getProjectWorkingDir(configPath: string) { + const pwd = path.dirname(await findNearestPackageJson(configPath)); + + if (!pwd) { + const msg = `${OutputKey} No workspace folder`; + vscode.window.showErrorMessage(msg); + outputChannel.appendLine(msg); + throw new Error(msg); + } + + return pwd; +} + +async function findNearestPackageJson(startPath: string) { + const rootPath = vscode.workspace.getWorkspaceFolder( + vscode.Uri.file(startPath), + )?.uri.fsPath; + + const msg = `${OutputKey} No root folder found. Unable to find package.json`; + + if (!rootPath) { + vscode.window.showErrorMessage(msg); + outputChannel.appendLine(msg); + throw new Error(msg); + } + + let currentDir = path.dirname(startPath); + + while (currentDir.startsWith(rootPath)) { + try { + const packageJsonPath = path.join(currentDir, "package.json"); + await vscode.workspace.fs.stat(vscode.Uri.file(packageJsonPath)); + return packageJsonPath; + } catch { + currentDir = path.dirname(currentDir); + } + } + + vscode.window.showErrorMessage(msg); + outputChannel.appendLine(msg); + throw new Error(msg); +} diff --git a/vscode-extension/src/extension.ts b/vscode-extension/src/extension.ts index 4994287..befa120 100644 --- a/vscode-extension/src/extension.ts +++ b/vscode-extension/src/extension.ts @@ -1,354 +1,42 @@ -import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process"; -import path from "node:path"; import * as vscode from "vscode"; import visualizerPkg from "../visualizer/package.json"; - -const outputChannel = vscode.window.createOutputChannel("Drizzle Visualizer"); -let currentPanel: vscode.WebviewPanel | undefined = undefined; -let visualizerServer: ChildProcessWithoutNullStreams | undefined = undefined; -let currentConfigPath: string | undefined = undefined; -let processIds: number[] = []; -let port: string | undefined = undefined; +import { setExtensionContext } from "./context"; +import { OpenVisualizerCodeLens } from "./modules/open-visualizer/codelens"; +import { OpenVisualizerCommand } from "./modules/open-visualizer/command"; +import { StopVisualizerCommand } from "./modules/stop-visualizer/command"; +import { stop } from "./server"; +import { outputChannel } from "./utils"; export function activate(context: vscode.ExtensionContext) { - const version = vscode.extensions.getExtension("rphlmr.vscode-drizzle-orm") - ?.packageJSON?.version; - outputChannel.appendLine(`Drizzle Visualizer activated: ${version}`); - outputChannel.appendLine(`Visualizer version: ${visualizerPkg.version}`); - outputChannel.appendLine(`Platform: ${process.platform}`); - outputChannel.appendLine(`Node version: ${process.version}`); - - if (Number(process.version.split("v")[1].split(".")[0]) < 20) { - vscode.window.showErrorMessage( - "Drizzle Visualizer requires Node.js 20 or higher", - ); - outputChannel.appendLine( - `Drizzle Visualizer requires Node.js 20 or higher, you have ${process.version}`, - ); - return; - } - - // Add CodeLens provider - const codeLensProvider = new (class implements vscode.CodeLensProvider { - async provideCodeLenses( - document: vscode.TextDocument, - ): Promise { - const text = document.getText(); - - // Check if file contains drizzle-related content - const isDrizzleFile = - text.includes("drizzle-kit") && - (text.includes("defineConfig") || - text.includes("type Config") || - text.includes("satisfies Config")); - - if (!isDrizzleFile) { - return []; - } - - // Find export statements that include defineConfig - const lines = text.split("\n"); - const configLines = lines - .map((line, index) => ({ line, index })) - .filter( - ({ line }) => - (line.includes("defineConfig") || line.includes("default")) && - (line.includes("export") || line.includes("module.exports")), - ); + setExtensionContext(context); + checkNodeVersion(); - return configLines.map(({ index }) => { - const range = new vscode.Range( - new vscode.Position(index, 0), - new vscode.Position(index, 0), - ); - - return new vscode.CodeLens(range, { - title: "🌧️ Open Drizzle Visualizer", - command: "drizzle.visualizer:start", - tooltip: "Open Drizzle Schema Visualizer", - arguments: [true], - }); - }); - } - })(); - - // Register the CodeLens provider + // Register CodeLens and commands context.subscriptions.push( - vscode.languages.registerCodeLensProvider( - { pattern: "**/*{drizzle,config}.ts" }, - codeLensProvider, - ), - ); - - const visualizerStart = vscode.commands.registerCommand( - "drizzle.visualizer:start", - async (isConfigFile: boolean) => { - const activeEditorUri = vscode.window.activeTextEditor?.document.uri; - - if (!activeEditorUri) { - vscode.window.showErrorMessage("No active editor"); - outputChannel.appendLine("[Server] No active editor"); - return; - } - - const cwd = path.dirname(await findNearestPackageJson(activeEditorUri)); - - if (!cwd) { - vscode.window.showErrorMessage("No workspace folder"); - outputChannel.appendLine("[Server] No workspace folder"); - return; - } - - outputChannel.appendLine(`[Server] Workspace folder: ${cwd}`); - - if (isConfigFile) { - outputChannel.appendLine( - `[Server] Loading config from: ${activeEditorUri.fsPath}`, - ); - } - - // If the config file has changed, restart the server - if ( - isConfigFile && - currentConfigPath && - currentConfigPath !== activeEditorUri.fsPath - ) { - outputChannel.appendLine( - `[Server] Config file changed. Killing server on port ${port} and restarting`, - ); - killServer(); - } - - currentConfigPath = activeEditorUri.fsPath; - - // If we already have a panel, show it instead of creating a new one - if (currentPanel) { - currentPanel.reveal(); - } - - // Create and show webview panel - currentPanel = - currentPanel || - vscode.window.createWebviewPanel( - "DrizzleVisualizer", - "Drizzle Visualizer", - vscode.ViewColumn.One, - { - enableScripts: true, - }, - ); - - currentPanel.iconPath = { - light: vscode.Uri.joinPath( - context.extensionUri, - "media", - "drizzle_dark.png", - ), - dark: vscode.Uri.joinPath(context.extensionUri, "media", "drizzle.png"), - }; - - if (!port) { - // random port from 60_000 to 64_000 - port = String(Math.floor(Math.random() * 4000) + 60000); - } - - const iframe = HTML(` -