|
| 1 | +import * as vscode from "vscode"; |
| 2 | +import * as path from "path"; |
| 3 | +import * as fs from "fs"; |
| 4 | +import { findProjectRootOfFileInDir } from "./utils"; |
| 5 | + |
| 6 | +export async function registerDynamicJsonValidation( |
| 7 | + outputChannel?: vscode.OutputChannel, |
| 8 | +): Promise<vscode.Disposable> { |
| 9 | + // Use workspace text document change events to detect when ReScript config files are opened |
| 10 | + const disposable = vscode.workspace.onDidOpenTextDocument( |
| 11 | + async (document) => { |
| 12 | + if (document.languageId === "json") { |
| 13 | + const fileName = path.basename(document.uri.fsPath); |
| 14 | + if (fileName === "rescript.json" || fileName === "bsconfig.json") { |
| 15 | + await tryEnableValidationForFile(document.uri, outputChannel); |
| 16 | + } |
| 17 | + } |
| 18 | + }, |
| 19 | + ); |
| 20 | + |
| 21 | + // Also check for already open documents |
| 22 | + const openDocuments = vscode.workspace.textDocuments; |
| 23 | + for (const document of openDocuments) { |
| 24 | + if (document.languageId === "json") { |
| 25 | + const fileName = path.basename(document.uri.fsPath); |
| 26 | + if (fileName === "rescript.json" || fileName === "bsconfig.json") { |
| 27 | + await tryEnableValidationForFile(document.uri, outputChannel); |
| 28 | + } |
| 29 | + } |
| 30 | + } |
| 31 | + |
| 32 | + return disposable; |
| 33 | +} |
| 34 | + |
| 35 | +async function tryEnableValidationForFile( |
| 36 | + uri: vscode.Uri, |
| 37 | + outputChannel?: vscode.OutputChannel, |
| 38 | +): Promise<void> { |
| 39 | + try { |
| 40 | + const projectRootDir = findProjectRootOfFileInDir(uri.fsPath); |
| 41 | + |
| 42 | + if (projectRootDir) { |
| 43 | + const schemaPath = path.join( |
| 44 | + projectRootDir, |
| 45 | + "node_modules", |
| 46 | + "rescript", |
| 47 | + "docs", |
| 48 | + "docson", |
| 49 | + "build-schema.json", |
| 50 | + ); |
| 51 | + |
| 52 | + if (fs.existsSync(schemaPath)) { |
| 53 | + const absoluteSchemaPath = "file://" + schemaPath; |
| 54 | + const fileName = path.basename(uri.fsPath); |
| 55 | + |
| 56 | + // Add this schema to the user's JSON validation settings |
| 57 | + const config = vscode.workspace.getConfiguration(); |
| 58 | + const jsonSchemas = |
| 59 | + config.get<Array<{ fileMatch: string[]; url: string }>>( |
| 60 | + "json.schemas", |
| 61 | + ) || []; |
| 62 | + |
| 63 | + // Remove existing ReScript schemas for this specific file |
| 64 | + const filteredSchemas = jsonSchemas.filter( |
| 65 | + (schema) => !schema.fileMatch || !schema.fileMatch.includes(fileName), |
| 66 | + ); |
| 67 | + |
| 68 | + // Add our new schema configuration |
| 69 | + const updatedSchemas = [ |
| 70 | + ...filteredSchemas, |
| 71 | + { |
| 72 | + fileMatch: [fileName], |
| 73 | + url: absoluteSchemaPath, |
| 74 | + }, |
| 75 | + ]; |
| 76 | + |
| 77 | + // Update the configuration globally to avoid workspace pollution |
| 78 | + await config.update( |
| 79 | + "json.schemas", |
| 80 | + updatedSchemas, |
| 81 | + vscode.ConfigurationTarget.Global, |
| 82 | + ); |
| 83 | + |
| 84 | + if (outputChannel) { |
| 85 | + outputChannel.appendLine(`JSON validation enabled for ${fileName}`); |
| 86 | + } |
| 87 | + } |
| 88 | + } |
| 89 | + } catch (error) { |
| 90 | + // Silently ignore errors to avoid annoying the user |
| 91 | + if (outputChannel) { |
| 92 | + outputChannel.appendLine( |
| 93 | + `Failed to enable JSON validation for ${uri.fsPath}: ${error}`, |
| 94 | + ); |
| 95 | + } |
| 96 | + } |
| 97 | +} |
| 98 | + |
0 commit comments