@prisma-next/language-server 0.14.0-dev.20 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { Connection } from "vscode-languageserver";
2
- import { DocumentAst, ParseDiagnostic, Range, SourceFile } from "@prisma-next/psl-parser/syntax";
3
2
  import { SymbolTable } from "@prisma-next/psl-parser";
3
+ import { DocumentAst, ParseDiagnostic, Range, SourceFile } from "@prisma-next/psl-parser/syntax";
4
4
  //#region src/diagnostic-mapping.d.ts
5
5
  declare const ParseDiagnosticSeverity: {
6
6
  readonly Error: 1;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/diagnostic-mapping.ts","../../src/project-artifacts.ts","../../src/server.ts","../../src/start-server.ts"],"mappings":";;;;cAEa,uBAAA;EAAA;;;;;UAOI,aAAA;EAAA,SACN,KAAA,EAAO,KAAK;EAAA,SACZ,OAAA;EAAA,SACA,IAAA;EAAA,SACA,QAAA;AAAA;AAAA,iBAGK,mBAAA,CACd,WAAA,WAAsB,eAAA,cACZ,aAAa;;;UCXR,cAAA;EAAA,SACN,QAAA,EAAU,WAAA;EAAA,SACV,UAAA,EAAY,UAAU;AAAA;;;UCqBhB,cAAA;EACf,OAAA;;AF7BF;;;EEkCE,cAAA,CAAe,GAAA,WAAc,cAAA;EAC7B,qBAAA,CAAsB,GAAA,WAAc,WAAW;AAAA;AAAA,iBAejC,YAAA,CAAa,UAAA,EAAY,UAAA,GAAa,cAAc;;;iBCjDpD,WAAA,IAAe,cAAc"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/diagnostic-mapping.ts","../../src/project-artifacts.ts","../../src/server.ts","../../src/start-server.ts"],"mappings":";;;;cAEa,uBAAA;EAAA;;;;;UAOI,aAAA;EAAA,SACN,KAAA,EAAO,KAAK;EAAA,SACZ,OAAA;EAAA,SACA,IAAA;EAAA,SACA,QAAA;AAAA;AAAA,iBAGK,mBAAA,CACd,WAAA,WAAsB,eAAA,cACZ,aAAa;;;UCXR,cAAA;EAAA,SACN,QAAA,EAAU,WAAA;EAAA,SACV,UAAA,EAAY,UAAU;AAAA;;;UCmBhB,cAAA;EACf,OAAA;;AF3BF;;;EEgCE,cAAA,CAAe,GAAA,WAAc,cAAA;EAC7B,qBAAA,CAAsB,GAAA,WAAc,WAAW;AAAA;AAAA,iBAejC,YAAA,CAAa,UAAA,EAAY,UAAA,GAAa,cAAc;;;iBC/CpD,WAAA,IAAe,cAAc"}
@@ -2,11 +2,11 @@ import { join } from "node:path";
2
2
  import { fileURLToPath, pathToFileURL } from "node:url";
3
3
  import { findNearestConfigPathForFile, loadConfig } from "@prisma-next/config-loader";
4
4
  import { format } from "@prisma-next/psl-parser/format";
5
- import { DiagnosticSeverity, DidChangeWatchedFilesNotification, FoldingRangeKind, RegistrationRequest, TextDocumentSyncKind, TextDocuments } from "vscode-languageserver";
5
+ import { DiagnosticSeverity, DidChangeWatchedFilesNotification, RegistrationRequest, TextDocumentSyncKind, TextDocuments } from "vscode-languageserver";
6
6
  import { TextDocument } from "vscode-languageserver-textdocument";
7
7
  import { createControlStack } from "@prisma-next/framework-components/control";
8
- import { NamespaceDeclarationAst, parse } from "@prisma-next/psl-parser/syntax";
9
8
  import { buildSymbolTable } from "@prisma-next/psl-parser";
9
+ import { parse } from "@prisma-next/psl-parser/syntax";
10
10
  import { ProposedFeatures, createConnection } from "vscode-languageserver/node";
11
11
  //#region src/diagnostic-mapping.ts
12
12
  const ParseDiagnosticSeverity = {
@@ -63,44 +63,6 @@ function resolveControlStackInputs(config) {
63
63
  };
64
64
  }
65
65
  //#endregion
66
- //#region src/folding-ranges.ts
67
- /**
68
- * Computes folding ranges for block declarations in a PSL document.
69
- *
70
- * Block types that produce folding ranges:
71
- * - model (e.g., `model User { ... }`)
72
- * - composite type (e.g., `type Address { ... }`)
73
- * - namespace (e.g., `namespace billing { ... }`)
74
- * - generic blocks (generator, datasource, extension blocks)
75
- * - types block (e.g., `types { ... }`)
76
- *
77
- * The range spans from the line containing `{` to the line containing `}`.
78
- */
79
- function computeFoldingRanges(document, sourceFile) {
80
- const ranges = [];
81
- collectFoldingRanges(document, sourceFile, ranges);
82
- return ranges;
83
- }
84
- function collectFoldingRanges(document, sourceFile, ranges) {
85
- for (const declaration of document.declarations()) {
86
- addFoldingRange(declaration, sourceFile, ranges);
87
- const namespace = NamespaceDeclarationAst.cast(declaration.syntax);
88
- if (namespace !== void 0) for (const nested of namespace.declarations()) addFoldingRange(nested, sourceFile, ranges);
89
- }
90
- }
91
- function addFoldingRange(declaration, sourceFile, ranges) {
92
- const lbrace = declaration.lbrace();
93
- const rbrace = declaration.rbrace();
94
- if (lbrace === void 0 || rbrace === void 0) return;
95
- const startLine = sourceFile.positionAt(lbrace.offset).line;
96
- const endLine = sourceFile.positionAt(rbrace.offset).line;
97
- ranges.push({
98
- startLine,
99
- endLine,
100
- kind: FoldingRangeKind.Region
101
- });
102
- }
103
- //#endregion
104
66
  //#region src/pipeline.ts
105
67
  /**
106
68
  * Composes the stages exactly as the contract-psl provider does, so the editor
@@ -325,8 +287,7 @@ function createServer(connection) {
325
287
  supportsWatchedFilesRegistration = clientSupportsWatchedFilesRegistration(params);
326
288
  return { capabilities: {
327
289
  textDocumentSync: TextDocumentSyncKind.Incremental,
328
- documentFormattingProvider: true,
329
- foldingRangeProvider: true
290
+ documentFormattingProvider: true
330
291
  } };
331
292
  });
332
293
  connection.onInitialized(() => {
@@ -350,18 +311,6 @@ function createServer(connection) {
350
311
  }
351
312
  });
352
313
  connection.onDocumentFormatting((params) => formatDocument(params.textDocument.uri));
353
- connection.onFoldingRanges(async (params) => {
354
- let project;
355
- try {
356
- project = await resolveProjectForDocument(params.textDocument.uri);
357
- } catch {
358
- return [];
359
- }
360
- if (project === void 0) return [];
361
- const cached = project.artifacts.getDocument(params.textDocument.uri);
362
- if (cached === void 0) return [];
363
- return computeFoldingRanges(cached.document, cached.sourceFile);
364
- });
365
314
  documents.onDidOpen((event) => {
366
315
  publishSafely(event.document.uri, event.document.getText());
367
316
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/diagnostic-mapping.ts","../../src/schema-inputs.ts","../../src/config-resolution.ts","../../src/folding-ranges.ts","../../src/pipeline.ts","../../src/document-diagnostics.ts","../../src/project-artifacts.ts","../../src/server.ts","../../src/start-server.ts"],"sourcesContent":["import type { ParseDiagnostic, Range } from '@prisma-next/psl-parser/syntax';\n\nexport const ParseDiagnosticSeverity = {\n Error: 1,\n Warning: 2,\n Information: 3,\n Hint: 4,\n} as const;\n\nexport interface LspDiagnostic {\n readonly range: Range;\n readonly message: string;\n readonly code: string;\n readonly severity: number;\n}\n\nexport function mapParseDiagnostics(\n diagnostics: readonly ParseDiagnostic[],\n): readonly LspDiagnostic[] {\n return diagnostics.map((diagnostic) => ({\n range: diagnostic.range,\n message: diagnostic.message,\n code: diagnostic.code,\n severity: ParseDiagnosticSeverity.Error,\n }));\n}\n","import { pathToFileURL } from 'node:url';\n\nexport interface SchemaInputConfig {\n readonly contract?: {\n readonly source: {\n readonly sourceFormat?: string;\n readonly inputs?: readonly string[];\n };\n };\n}\n\nexport interface SchemaInputSet {\n includes(uri: string): boolean;\n}\n\nexport function hasPslInputs(config: SchemaInputConfig): boolean {\n const source = config.contract?.source;\n return source?.sourceFormat === 'psl' && source.inputs !== undefined;\n}\n\nexport function resolveSchemaInputs(config: SchemaInputConfig): SchemaInputSet {\n const inputs = hasPslInputs(config) ? config.contract?.source.inputs : undefined;\n const uris = new Set(inputs?.map((input) => pathToFileURL(input).toString()));\n\n return {\n includes: (uri) => uris.has(uri),\n };\n}\n\nexport const emptySchemaInputSet: SchemaInputSet = {\n includes: () => false,\n};\n","import { loadConfig, type PrismaNextConfig } from '@prisma-next/config-loader';\nimport { createControlStack } from '@prisma-next/framework-components/control';\nimport type { FormatOptions } from '@prisma-next/psl-parser/format';\nimport type { PipelineInputs } from './pipeline';\nimport { hasPslInputs, resolveSchemaInputs, type SchemaInputSet } from './schema-inputs';\n\nexport const CONFIG_FILENAME = 'prisma-next.config.ts';\n\nexport interface ConfigResolution {\n readonly inputs: SchemaInputSet;\n readonly formatter?: FormatOptions;\n readonly controlStack: PipelineInputs;\n}\n\nconst emptyPipelineInputs: PipelineInputs = {\n scalarTypes: [],\n pslBlockDescriptors: {},\n};\n\nexport async function resolveConfigInputs(configPath: string): Promise<ConfigResolution> {\n const config = await loadConfig(configPath);\n const inputs = resolveSchemaInputs(config);\n const controlStack = resolveControlStackInputs(config) ?? emptyPipelineInputs;\n return config.formatter === undefined\n ? { inputs, controlStack }\n : { inputs, formatter: config.formatter, controlStack };\n}\n\nexport function resolveControlStackInputs(config: PrismaNextConfig): PipelineInputs | undefined {\n if (!hasPslInputs(config)) {\n return undefined;\n }\n const stack = createControlStack(config);\n return {\n scalarTypes: [...stack.scalarTypeDescriptors.keys()],\n pslBlockDescriptors: stack.authoringContributions.pslBlockDescriptors,\n };\n}\n","import {\n type DocumentAst,\n NamespaceDeclarationAst,\n type NamespaceMemberAst,\n type SourceFile,\n type TypesBlockAst,\n} from '@prisma-next/psl-parser/syntax';\nimport { type FoldingRange, FoldingRangeKind } from 'vscode-languageserver';\n\ntype Declaration = NamespaceMemberAst | TypesBlockAst | NamespaceDeclarationAst;\n\n/**\n * Computes folding ranges for block declarations in a PSL document.\n *\n * Block types that produce folding ranges:\n * - model (e.g., `model User { ... }`)\n * - composite type (e.g., `type Address { ... }`)\n * - namespace (e.g., `namespace billing { ... }`)\n * - generic blocks (generator, datasource, extension blocks)\n * - types block (e.g., `types { ... }`)\n *\n * The range spans from the line containing `{` to the line containing `}`.\n */\nexport function computeFoldingRanges(\n document: DocumentAst,\n sourceFile: SourceFile,\n): FoldingRange[] {\n const ranges: FoldingRange[] = [];\n collectFoldingRanges(document, sourceFile, ranges);\n return ranges;\n}\n\nfunction collectFoldingRanges(\n document: DocumentAst,\n sourceFile: SourceFile,\n ranges: FoldingRange[],\n): void {\n for (const declaration of document.declarations()) {\n addFoldingRange(declaration, sourceFile, ranges);\n\n const namespace = NamespaceDeclarationAst.cast(declaration.syntax);\n if (namespace !== undefined) {\n for (const nested of namespace.declarations()) {\n addFoldingRange(nested, sourceFile, ranges);\n }\n }\n }\n}\n\nfunction addFoldingRange(\n declaration: Declaration,\n sourceFile: SourceFile,\n ranges: FoldingRange[],\n): void {\n const lbrace = declaration.lbrace();\n const rbrace = declaration.rbrace();\n\n if (lbrace === undefined || rbrace === undefined) {\n return;\n }\n\n const startLine = sourceFile.positionAt(lbrace.offset).line;\n const endLine = sourceFile.positionAt(rbrace.offset).line;\n\n ranges.push({\n startLine,\n endLine,\n kind: FoldingRangeKind.Region,\n });\n}\n","import type { AuthoringPslBlockDescriptorNamespace } from '@prisma-next/framework-components/authoring';\nimport { buildSymbolTable, type SymbolTable } from '@prisma-next/psl-parser';\nimport { type DocumentAst, parse, type SourceFile } from '@prisma-next/psl-parser/syntax';\nimport { type LspDiagnostic, mapParseDiagnostics } from './diagnostic-mapping';\n\n/**\n * `pslBlockDescriptors` is kept complete on the live path so extension-block\n * validation matches the build; the structural diagnostics (duplicate\n * declaration, invalid qualified type) hold even without descriptors.\n */\nexport interface PipelineInputs {\n readonly scalarTypes: readonly string[];\n readonly pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace;\n}\n\nexport interface PipelineResult {\n readonly document: DocumentAst;\n readonly sourceFile: SourceFile;\n readonly symbolTable: SymbolTable;\n readonly diagnostics: readonly LspDiagnostic[];\n}\n\n/**\n * Composes the stages exactly as the contract-psl provider does, so the editor\n * and the build agree: `parse` then `buildSymbolTable`, parse diagnostics ahead\n * of symbol-table diagnostics. Never throws on malformed input — `parse`\n * recovers and `buildSymbolTable` is documented not to throw.\n */\nexport function runPipeline(text: string, inputs: PipelineInputs): PipelineResult {\n const { document, sourceFile, diagnostics: parseDiagnostics } = parse(text);\n const { table: symbolTable, diagnostics: symbolTableDiagnostics } = buildSymbolTable({\n document,\n sourceFile,\n scalarTypes: inputs.scalarTypes,\n pslBlockDescriptors: inputs.pslBlockDescriptors,\n });\n\n return {\n document,\n sourceFile,\n symbolTable,\n diagnostics: mapParseDiagnostics([...parseDiagnostics, ...symbolTableDiagnostics]),\n };\n}\n","import type { SymbolTable } from '@prisma-next/psl-parser';\nimport type { DocumentAst, SourceFile } from '@prisma-next/psl-parser/syntax';\nimport type { LspDiagnostic } from './diagnostic-mapping';\nimport { type PipelineInputs, runPipeline } from './pipeline';\nimport type { SchemaInputSet } from './schema-inputs';\n\nexport interface DocumentDiagnostics {\n readonly diagnostics: readonly LspDiagnostic[];\n readonly document: DocumentAst;\n readonly sourceFile: SourceFile;\n readonly symbolTable: SymbolTable;\n}\n\n/**\n * `null` (not a configured input) is distinct from a `DocumentDiagnostics` whose\n * `diagnostics` are `[]` (an input that parsed clean): the caller treats both as\n * \"publish no diagnostics\", but only the latter is a document we own and keep\n * diagnosing.\n */\nexport function computeDocumentDiagnostics(\n uri: string,\n text: string,\n inputs: SchemaInputSet,\n controlStack: PipelineInputs,\n): DocumentDiagnostics | null {\n if (!inputs.includes(uri)) {\n return null;\n }\n const { document, sourceFile, symbolTable, diagnostics } = runPipeline(text, controlStack);\n return { diagnostics, document, sourceFile, symbolTable };\n}\n","import type { SymbolTable } from '@prisma-next/psl-parser';\nimport type { DocumentAst, SourceFile } from '@prisma-next/psl-parser/syntax';\nimport type { LspDiagnostic } from './diagnostic-mapping';\nimport { computeDocumentDiagnostics } from './document-diagnostics';\nimport type { PipelineInputs } from './pipeline';\nimport type { SchemaInputSet } from './schema-inputs';\n\nexport interface CachedDocument {\n readonly document: DocumentAst;\n readonly sourceFile: SourceFile;\n}\n\nexport interface ProjectArtifacts {\n getDocument(uri: string): CachedDocument | undefined;\n getSymbolTable(): SymbolTable | undefined;\n update(\n uri: string,\n text: string,\n inputs: SchemaInputSet,\n controlStack: PipelineInputs,\n ): readonly LspDiagnostic[] | null;\n remove(uri: string): void;\n}\n\nexport function createProjectArtifacts(): ProjectArtifacts {\n const documents = new Map<string, CachedDocument>();\n let symbolTable: SymbolTable | undefined;\n\n return {\n getDocument: (uri) => documents.get(uri),\n getSymbolTable: () => symbolTable,\n update: (uri, text, inputs, controlStack) => {\n const computed = computeDocumentDiagnostics(uri, text, inputs, controlStack);\n if (computed === null) {\n if (documents.delete(uri)) {\n symbolTable = undefined;\n }\n return null;\n }\n documents.set(uri, {\n document: computed.document,\n sourceFile: computed.sourceFile,\n });\n // One symbol table per project. Single-input reality: it is (re)built from\n // the one open configured input on every edit. Merging several open inputs\n // into one project table — and reading unopened `inputs` from disk — is the\n // deferred cross-file work; `buildSymbolTable`'s single-document signature\n // is left untouched for it.\n symbolTable = computed.symbolTable;\n return computed.diagnostics;\n },\n remove: (uri) => {\n if (documents.delete(uri)) {\n symbolTable = undefined;\n }\n },\n };\n}\n","import { join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { findNearestConfigPathForFile } from '@prisma-next/config-loader';\nimport type { SymbolTable } from '@prisma-next/psl-parser';\nimport { type FormatOptions, format } from '@prisma-next/psl-parser/format';\nimport {\n type Connection,\n type Diagnostic,\n DiagnosticSeverity,\n DidChangeWatchedFilesNotification,\n type FoldingRange,\n type InitializeParams,\n type InitializeResult,\n RegistrationRequest,\n TextDocumentSyncKind,\n TextDocuments,\n type TextEdit,\n} from 'vscode-languageserver';\nimport { TextDocument } from 'vscode-languageserver-textdocument';\nimport { CONFIG_FILENAME, resolveConfigInputs } from './config-resolution';\nimport { ParseDiagnosticSeverity } from './diagnostic-mapping';\nimport { computeFoldingRanges } from './folding-ranges';\nimport type { PipelineInputs } from './pipeline';\nimport {\n type CachedDocument,\n createProjectArtifacts,\n type ProjectArtifacts,\n} from './project-artifacts';\nimport type { SchemaInputSet } from './schema-inputs';\n\nexport interface LanguageServer {\n dispose(): void;\n /**\n * Exposed for future features (completion, semantic tokens); nothing consumes\n * them yet.\n */\n getDocumentAst(uri: string): CachedDocument | undefined;\n getProjectSymbolTable(uri: string): SymbolTable | undefined;\n}\n\ninterface ProjectState {\n readonly configPath: string;\n readonly inputs: SchemaInputSet;\n readonly formatter?: FormatOptions;\n /**\n * Resolved once per config and refreshed by the config-watch path — never\n * rebuilt per document.\n */\n readonly controlStack: PipelineInputs;\n readonly artifacts: ProjectArtifacts;\n}\n\nexport function createServer(connection: Connection): LanguageServer {\n const documents = new TextDocuments(TextDocument);\n const projects = new Map<string, ProjectState>();\n const projectLoads = new Map<string, Promise<ProjectState>>();\n const documentConfigPaths = new Map<string, string>();\n let rootPath = process.cwd();\n let watchedConfigGlob = join(rootPath, '**', CONFIG_FILENAME);\n let supportsWatchedFilesRegistration = false;\n\n async function publish(uri: string, text: string): Promise<void> {\n const project = await resolveProjectForDocument(uri);\n if (project === undefined) {\n return;\n }\n const currentDocument = documents.get(uri);\n if (currentDocument === undefined) {\n documentConfigPaths.delete(uri);\n return;\n }\n if (currentDocument.getText() !== text) {\n return;\n }\n const computed = project.artifacts.update(uri, text, project.inputs, project.controlStack);\n if (computed === null) {\n void connection.sendDiagnostics({ uri, diagnostics: [] });\n return;\n }\n const diagnostics: Diagnostic[] = computed.map((diagnostic) => ({\n range: diagnostic.range,\n message: diagnostic.message,\n code: diagnostic.code,\n severity: toLspSeverity(diagnostic.severity),\n source: 'prisma-next',\n }));\n void connection.sendDiagnostics({ uri, diagnostics });\n }\n\n async function resolveProjectForDocument(uri: string): Promise<ProjectState | undefined> {\n const knownConfigPath = documentConfigPaths.get(uri);\n if (knownConfigPath !== undefined) {\n return resolveProject(knownConfigPath);\n }\n\n const filePath = filePathFromUri(uri);\n if (filePath === undefined) {\n return undefined;\n }\n\n const configPath = await findNearestConfigPathForFile(filePath);\n if (configPath === undefined) {\n return undefined;\n }\n\n documentConfigPaths.set(uri, configPath);\n const project = await resolveProjectIfLoadable(configPath);\n if (project === undefined) {\n documentConfigPaths.delete(uri);\n }\n return project;\n }\n\n async function resolveProjectIfLoadable(configPath: string): Promise<ProjectState | undefined> {\n try {\n return await resolveProject(configPath);\n } catch {\n stopManagingProject(configPath);\n return undefined;\n }\n }\n\n async function resolveProject(configPath: string): Promise<ProjectState> {\n const existing = projects.get(configPath);\n if (existing !== undefined) {\n return existing;\n }\n const existingLoad = projectLoads.get(configPath);\n if (existingLoad !== undefined) {\n return existingLoad;\n }\n return queueProjectLoad(configPath);\n }\n\n function refreshProject(configPath: string): Promise<ProjectState> {\n return queueProjectLoad(configPath);\n }\n\n function queueProjectLoad(configPath: string): Promise<ProjectState> {\n const previousLoad = projectLoads.get(configPath) ?? Promise.resolve();\n const load = previousLoad\n .catch(() => undefined)\n .then(() => loadProject(configPath))\n .finally(() => {\n if (projectLoads.get(configPath) === load) {\n projectLoads.delete(configPath);\n }\n });\n projectLoads.set(configPath, load);\n return load;\n }\n\n async function loadProject(configPath: string): Promise<ProjectState> {\n const resolution = await resolveConfigInputs(configPath);\n // Preserve open-document ASTs across config reloads; the project symbol table\n // refreshes on the next publish against the new stack.\n const artifacts = projects.get(configPath)?.artifacts ?? createProjectArtifacts();\n const project: ProjectState =\n resolution.formatter === undefined\n ? {\n configPath,\n inputs: resolution.inputs,\n controlStack: resolution.controlStack,\n artifacts,\n }\n : {\n configPath,\n inputs: resolution.inputs,\n formatter: resolution.formatter,\n controlStack: resolution.controlStack,\n artifacts,\n };\n projects.set(configPath, project);\n return project;\n }\n\n function stopManagingProject(configPath: string): void {\n const hadProject = projects.delete(configPath);\n for (const document of documents.all()) {\n if (documentConfigPaths.get(document.uri) === configPath) {\n documentConfigPaths.delete(document.uri);\n if (hadProject) {\n void connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });\n }\n }\n }\n }\n\n async function republishOpenDocumentsForConfig(configPath: string): Promise<void> {\n for (const document of documents.all()) {\n const knownConfigPath = documentConfigPaths.get(document.uri);\n if (knownConfigPath === configPath) {\n await publish(document.uri, document.getText());\n continue;\n }\n\n const filePath = filePathFromUri(document.uri);\n if (filePath === undefined) {\n continue;\n }\n const nearestConfigPath = await findNearestConfigPathForFile(filePath);\n if (nearestConfigPath === configPath) {\n documentConfigPaths.set(document.uri, configPath);\n await publish(document.uri, document.getText());\n }\n }\n }\n\n function publishSafely(uri: string, text: string): void {\n void publish(uri, text).catch((error: unknown) => {\n connection.console.error(error instanceof Error ? error.message : String(error));\n });\n }\n\n async function formatDocument(uri: string): Promise<TextEdit[]> {\n const document = documents.get(uri);\n if (document === undefined) {\n return [];\n }\n\n let project: ProjectState | undefined;\n try {\n project = await resolveProjectForDocument(uri);\n } catch {\n return [];\n }\n if (project === undefined || !project.inputs.includes(uri)) {\n return [];\n }\n\n const source = document.getText();\n let formatted: string;\n try {\n formatted = format(source, project.formatter);\n } catch {\n return [];\n }\n\n if (formatted === source) {\n return [];\n }\n\n return [\n {\n range: { start: { line: 0, character: 0 }, end: document.positionAt(source.length) },\n newText: formatted,\n },\n ];\n }\n\n connection.onInitialize(async (params): Promise<InitializeResult> => {\n rootPath = resolveRootPath(params.rootUri, params.rootPath);\n watchedConfigGlob = join(rootPath, '**', CONFIG_FILENAME);\n supportsWatchedFilesRegistration = clientSupportsWatchedFilesRegistration(params);\n\n return {\n capabilities: {\n textDocumentSync: TextDocumentSyncKind.Incremental,\n documentFormattingProvider: true,\n foldingRangeProvider: true,\n },\n };\n });\n\n connection.onInitialized(() => {\n if (supportsWatchedFilesRegistration) {\n void connection\n .sendRequest(RegistrationRequest.type, {\n registrations: [\n {\n id: 'prisma-next-config-watcher',\n method: DidChangeWatchedFilesNotification.type.method,\n registerOptions: { watchers: [{ globPattern: watchedConfigGlob }] },\n },\n ],\n })\n .catch(() => undefined);\n } else {\n connection.console.warn(\n 'Client does not support dynamic file-watcher registration; Prisma Next config changes will not be picked up without a restart.',\n );\n }\n });\n\n connection.onDidChangeWatchedFiles(async (params) => {\n const changedConfigPaths = configPathsFromWatchedChanges(\n params.changes.map((change) => filePathFromUri(change.uri)),\n );\n for (const configPath of changedConfigPaths) {\n try {\n await refreshProject(configPath);\n } catch {\n stopManagingProject(configPath);\n continue;\n }\n await republishOpenDocumentsForConfig(configPath);\n }\n });\n\n connection.onDocumentFormatting((params) => formatDocument(params.textDocument.uri));\n\n connection.onFoldingRanges(async (params): Promise<FoldingRange[]> => {\n let project: ProjectState | undefined;\n try {\n project = await resolveProjectForDocument(params.textDocument.uri);\n } catch {\n return [];\n }\n if (project === undefined) {\n return [];\n }\n const cached = project.artifacts.getDocument(params.textDocument.uri);\n if (cached === undefined) {\n return [];\n }\n return computeFoldingRanges(cached.document, cached.sourceFile);\n });\n\n documents.onDidOpen((event) => {\n publishSafely(event.document.uri, event.document.getText());\n });\n documents.onDidChangeContent((event) => {\n publishSafely(event.document.uri, event.document.getText());\n });\n documents.onDidClose((event) => {\n const uri = event.document.uri;\n const configPath = documentConfigPaths.get(uri);\n if (configPath !== undefined) {\n projects.get(configPath)?.artifacts.remove(uri);\n }\n documentConfigPaths.delete(uri);\n void connection.sendDiagnostics({ uri, diagnostics: [] });\n });\n\n documents.listen(connection);\n connection.listen();\n\n function artifactsForDocument(uri: string): ProjectArtifacts | undefined {\n const configPath = documentConfigPaths.get(uri);\n return configPath === undefined ? undefined : projects.get(configPath)?.artifacts;\n }\n\n return {\n dispose: () => {\n connection.dispose();\n },\n getDocumentAst: (uri) => artifactsForDocument(uri)?.getDocument(uri),\n getProjectSymbolTable: (uri) => artifactsForDocument(uri)?.getSymbolTable(),\n };\n}\n\nfunction toLspSeverity(severity: number): DiagnosticSeverity {\n switch (severity) {\n case ParseDiagnosticSeverity.Warning:\n return DiagnosticSeverity.Warning;\n case ParseDiagnosticSeverity.Information:\n return DiagnosticSeverity.Information;\n case ParseDiagnosticSeverity.Hint:\n return DiagnosticSeverity.Hint;\n default:\n return DiagnosticSeverity.Error;\n }\n}\n\nfunction clientSupportsWatchedFilesRegistration(params: InitializeParams): boolean {\n return params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration === true;\n}\n\nfunction resolveRootPath(\n rootUri: string | null | undefined,\n rootPath: string | null | undefined,\n): string {\n if (rootUri) {\n return fileURLToPath(rootUri);\n }\n if (rootPath) {\n return rootPath;\n }\n return process.cwd();\n}\n\nfunction filePathFromUri(uri: string): string | undefined {\n try {\n return fileURLToPath(uri);\n } catch {\n return undefined;\n }\n}\n\nfunction configPathsFromWatchedChanges(paths: readonly (string | undefined)[]): Set<string> {\n const configPaths = new Set<string>();\n for (const path of paths) {\n if (path?.endsWith(CONFIG_FILENAME)) {\n configPaths.add(path);\n }\n }\n return configPaths;\n}\n","import { createConnection, ProposedFeatures } from 'vscode-languageserver/node';\nimport { createServer, type LanguageServer } from './server';\n\nexport function startServer(): LanguageServer {\n const connection = createConnection(ProposedFeatures.all);\n return createServer(connection);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/diagnostic-mapping.ts","../../src/schema-inputs.ts","../../src/config-resolution.ts","../../src/pipeline.ts","../../src/document-diagnostics.ts","../../src/project-artifacts.ts","../../src/server.ts","../../src/start-server.ts"],"sourcesContent":["import type { ParseDiagnostic, Range } from '@prisma-next/psl-parser/syntax';\n\nexport const ParseDiagnosticSeverity = {\n Error: 1,\n Warning: 2,\n Information: 3,\n Hint: 4,\n} as const;\n\nexport interface LspDiagnostic {\n readonly range: Range;\n readonly message: string;\n readonly code: string;\n readonly severity: number;\n}\n\nexport function mapParseDiagnostics(\n diagnostics: readonly ParseDiagnostic[],\n): readonly LspDiagnostic[] {\n return diagnostics.map((diagnostic) => ({\n range: diagnostic.range,\n message: diagnostic.message,\n code: diagnostic.code,\n severity: ParseDiagnosticSeverity.Error,\n }));\n}\n","import { pathToFileURL } from 'node:url';\n\nexport interface SchemaInputConfig {\n readonly contract?: {\n readonly source: {\n readonly sourceFormat?: string;\n readonly inputs?: readonly string[];\n };\n };\n}\n\nexport interface SchemaInputSet {\n includes(uri: string): boolean;\n}\n\nexport function hasPslInputs(config: SchemaInputConfig): boolean {\n const source = config.contract?.source;\n return source?.sourceFormat === 'psl' && source.inputs !== undefined;\n}\n\nexport function resolveSchemaInputs(config: SchemaInputConfig): SchemaInputSet {\n const inputs = hasPslInputs(config) ? config.contract?.source.inputs : undefined;\n const uris = new Set(inputs?.map((input) => pathToFileURL(input).toString()));\n\n return {\n includes: (uri) => uris.has(uri),\n };\n}\n\nexport const emptySchemaInputSet: SchemaInputSet = {\n includes: () => false,\n};\n","import { loadConfig, type PrismaNextConfig } from '@prisma-next/config-loader';\nimport { createControlStack } from '@prisma-next/framework-components/control';\nimport type { FormatOptions } from '@prisma-next/psl-parser/format';\nimport type { PipelineInputs } from './pipeline';\nimport { hasPslInputs, resolveSchemaInputs, type SchemaInputSet } from './schema-inputs';\n\nexport const CONFIG_FILENAME = 'prisma-next.config.ts';\n\nexport interface ConfigResolution {\n readonly inputs: SchemaInputSet;\n readonly formatter?: FormatOptions;\n readonly controlStack: PipelineInputs;\n}\n\nconst emptyPipelineInputs: PipelineInputs = {\n scalarTypes: [],\n pslBlockDescriptors: {},\n};\n\nexport async function resolveConfigInputs(configPath: string): Promise<ConfigResolution> {\n const config = await loadConfig(configPath);\n const inputs = resolveSchemaInputs(config);\n const controlStack = resolveControlStackInputs(config) ?? emptyPipelineInputs;\n return config.formatter === undefined\n ? { inputs, controlStack }\n : { inputs, formatter: config.formatter, controlStack };\n}\n\nexport function resolveControlStackInputs(config: PrismaNextConfig): PipelineInputs | undefined {\n if (!hasPslInputs(config)) {\n return undefined;\n }\n const stack = createControlStack(config);\n return {\n scalarTypes: [...stack.scalarTypeDescriptors.keys()],\n pslBlockDescriptors: stack.authoringContributions.pslBlockDescriptors,\n };\n}\n","import type { AuthoringPslBlockDescriptorNamespace } from '@prisma-next/framework-components/authoring';\nimport { buildSymbolTable, type SymbolTable } from '@prisma-next/psl-parser';\nimport { type DocumentAst, parse, type SourceFile } from '@prisma-next/psl-parser/syntax';\nimport { type LspDiagnostic, mapParseDiagnostics } from './diagnostic-mapping';\n\n/**\n * `pslBlockDescriptors` is kept complete on the live path so extension-block\n * validation matches the build; the structural diagnostics (duplicate\n * declaration, invalid qualified type) hold even without descriptors.\n */\nexport interface PipelineInputs {\n readonly scalarTypes: readonly string[];\n readonly pslBlockDescriptors: AuthoringPslBlockDescriptorNamespace;\n}\n\nexport interface PipelineResult {\n readonly document: DocumentAst;\n readonly sourceFile: SourceFile;\n readonly symbolTable: SymbolTable;\n readonly diagnostics: readonly LspDiagnostic[];\n}\n\n/**\n * Composes the stages exactly as the contract-psl provider does, so the editor\n * and the build agree: `parse` then `buildSymbolTable`, parse diagnostics ahead\n * of symbol-table diagnostics. Never throws on malformed input — `parse`\n * recovers and `buildSymbolTable` is documented not to throw.\n */\nexport function runPipeline(text: string, inputs: PipelineInputs): PipelineResult {\n const { document, sourceFile, diagnostics: parseDiagnostics } = parse(text);\n const { table: symbolTable, diagnostics: symbolTableDiagnostics } = buildSymbolTable({\n document,\n sourceFile,\n scalarTypes: inputs.scalarTypes,\n pslBlockDescriptors: inputs.pslBlockDescriptors,\n });\n\n return {\n document,\n sourceFile,\n symbolTable,\n diagnostics: mapParseDiagnostics([...parseDiagnostics, ...symbolTableDiagnostics]),\n };\n}\n","import type { SymbolTable } from '@prisma-next/psl-parser';\nimport type { DocumentAst, SourceFile } from '@prisma-next/psl-parser/syntax';\nimport type { LspDiagnostic } from './diagnostic-mapping';\nimport { type PipelineInputs, runPipeline } from './pipeline';\nimport type { SchemaInputSet } from './schema-inputs';\n\nexport interface DocumentDiagnostics {\n readonly diagnostics: readonly LspDiagnostic[];\n readonly document: DocumentAst;\n readonly sourceFile: SourceFile;\n readonly symbolTable: SymbolTable;\n}\n\n/**\n * `null` (not a configured input) is distinct from a `DocumentDiagnostics` whose\n * `diagnostics` are `[]` (an input that parsed clean): the caller treats both as\n * \"publish no diagnostics\", but only the latter is a document we own and keep\n * diagnosing.\n */\nexport function computeDocumentDiagnostics(\n uri: string,\n text: string,\n inputs: SchemaInputSet,\n controlStack: PipelineInputs,\n): DocumentDiagnostics | null {\n if (!inputs.includes(uri)) {\n return null;\n }\n const { document, sourceFile, symbolTable, diagnostics } = runPipeline(text, controlStack);\n return { diagnostics, document, sourceFile, symbolTable };\n}\n","import type { SymbolTable } from '@prisma-next/psl-parser';\nimport type { DocumentAst, SourceFile } from '@prisma-next/psl-parser/syntax';\nimport type { LspDiagnostic } from './diagnostic-mapping';\nimport { computeDocumentDiagnostics } from './document-diagnostics';\nimport type { PipelineInputs } from './pipeline';\nimport type { SchemaInputSet } from './schema-inputs';\n\nexport interface CachedDocument {\n readonly document: DocumentAst;\n readonly sourceFile: SourceFile;\n}\n\nexport interface ProjectArtifacts {\n getDocument(uri: string): CachedDocument | undefined;\n getSymbolTable(): SymbolTable | undefined;\n update(\n uri: string,\n text: string,\n inputs: SchemaInputSet,\n controlStack: PipelineInputs,\n ): readonly LspDiagnostic[] | null;\n remove(uri: string): void;\n}\n\nexport function createProjectArtifacts(): ProjectArtifacts {\n const documents = new Map<string, CachedDocument>();\n let symbolTable: SymbolTable | undefined;\n\n return {\n getDocument: (uri) => documents.get(uri),\n getSymbolTable: () => symbolTable,\n update: (uri, text, inputs, controlStack) => {\n const computed = computeDocumentDiagnostics(uri, text, inputs, controlStack);\n if (computed === null) {\n if (documents.delete(uri)) {\n symbolTable = undefined;\n }\n return null;\n }\n documents.set(uri, {\n document: computed.document,\n sourceFile: computed.sourceFile,\n });\n // One symbol table per project. Single-input reality: it is (re)built from\n // the one open configured input on every edit. Merging several open inputs\n // into one project table — and reading unopened `inputs` from disk — is the\n // deferred cross-file work; `buildSymbolTable`'s single-document signature\n // is left untouched for it.\n symbolTable = computed.symbolTable;\n return computed.diagnostics;\n },\n remove: (uri) => {\n if (documents.delete(uri)) {\n symbolTable = undefined;\n }\n },\n };\n}\n","import { join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { findNearestConfigPathForFile } from '@prisma-next/config-loader';\nimport type { SymbolTable } from '@prisma-next/psl-parser';\nimport { type FormatOptions, format } from '@prisma-next/psl-parser/format';\nimport {\n type Connection,\n type Diagnostic,\n DiagnosticSeverity,\n DidChangeWatchedFilesNotification,\n type InitializeParams,\n type InitializeResult,\n RegistrationRequest,\n TextDocumentSyncKind,\n TextDocuments,\n type TextEdit,\n} from 'vscode-languageserver';\nimport { TextDocument } from 'vscode-languageserver-textdocument';\nimport { CONFIG_FILENAME, resolveConfigInputs } from './config-resolution';\nimport { ParseDiagnosticSeverity } from './diagnostic-mapping';\nimport type { PipelineInputs } from './pipeline';\nimport {\n type CachedDocument,\n createProjectArtifacts,\n type ProjectArtifacts,\n} from './project-artifacts';\nimport type { SchemaInputSet } from './schema-inputs';\n\nexport interface LanguageServer {\n dispose(): void;\n /**\n * Exposed for future features (completion, semantic tokens); nothing consumes\n * them yet.\n */\n getDocumentAst(uri: string): CachedDocument | undefined;\n getProjectSymbolTable(uri: string): SymbolTable | undefined;\n}\n\ninterface ProjectState {\n readonly configPath: string;\n readonly inputs: SchemaInputSet;\n readonly formatter?: FormatOptions;\n /**\n * Resolved once per config and refreshed by the config-watch path — never\n * rebuilt per document.\n */\n readonly controlStack: PipelineInputs;\n readonly artifacts: ProjectArtifacts;\n}\n\nexport function createServer(connection: Connection): LanguageServer {\n const documents = new TextDocuments(TextDocument);\n const projects = new Map<string, ProjectState>();\n const projectLoads = new Map<string, Promise<ProjectState>>();\n const documentConfigPaths = new Map<string, string>();\n let rootPath = process.cwd();\n let watchedConfigGlob = join(rootPath, '**', CONFIG_FILENAME);\n let supportsWatchedFilesRegistration = false;\n\n async function publish(uri: string, text: string): Promise<void> {\n const project = await resolveProjectForDocument(uri);\n if (project === undefined) {\n return;\n }\n const currentDocument = documents.get(uri);\n if (currentDocument === undefined) {\n documentConfigPaths.delete(uri);\n return;\n }\n if (currentDocument.getText() !== text) {\n return;\n }\n const computed = project.artifacts.update(uri, text, project.inputs, project.controlStack);\n if (computed === null) {\n void connection.sendDiagnostics({ uri, diagnostics: [] });\n return;\n }\n const diagnostics: Diagnostic[] = computed.map((diagnostic) => ({\n range: diagnostic.range,\n message: diagnostic.message,\n code: diagnostic.code,\n severity: toLspSeverity(diagnostic.severity),\n source: 'prisma-next',\n }));\n void connection.sendDiagnostics({ uri, diagnostics });\n }\n\n async function resolveProjectForDocument(uri: string): Promise<ProjectState | undefined> {\n const knownConfigPath = documentConfigPaths.get(uri);\n if (knownConfigPath !== undefined) {\n return resolveProject(knownConfigPath);\n }\n\n const filePath = filePathFromUri(uri);\n if (filePath === undefined) {\n return undefined;\n }\n\n const configPath = await findNearestConfigPathForFile(filePath);\n if (configPath === undefined) {\n return undefined;\n }\n\n documentConfigPaths.set(uri, configPath);\n const project = await resolveProjectIfLoadable(configPath);\n if (project === undefined) {\n documentConfigPaths.delete(uri);\n }\n return project;\n }\n\n async function resolveProjectIfLoadable(configPath: string): Promise<ProjectState | undefined> {\n try {\n return await resolveProject(configPath);\n } catch {\n stopManagingProject(configPath);\n return undefined;\n }\n }\n\n async function resolveProject(configPath: string): Promise<ProjectState> {\n const existing = projects.get(configPath);\n if (existing !== undefined) {\n return existing;\n }\n const existingLoad = projectLoads.get(configPath);\n if (existingLoad !== undefined) {\n return existingLoad;\n }\n return queueProjectLoad(configPath);\n }\n\n function refreshProject(configPath: string): Promise<ProjectState> {\n return queueProjectLoad(configPath);\n }\n\n function queueProjectLoad(configPath: string): Promise<ProjectState> {\n const previousLoad = projectLoads.get(configPath) ?? Promise.resolve();\n const load = previousLoad\n .catch(() => undefined)\n .then(() => loadProject(configPath))\n .finally(() => {\n if (projectLoads.get(configPath) === load) {\n projectLoads.delete(configPath);\n }\n });\n projectLoads.set(configPath, load);\n return load;\n }\n\n async function loadProject(configPath: string): Promise<ProjectState> {\n const resolution = await resolveConfigInputs(configPath);\n // Preserve open-document ASTs across config reloads; the project symbol table\n // refreshes on the next publish against the new stack.\n const artifacts = projects.get(configPath)?.artifacts ?? createProjectArtifacts();\n const project: ProjectState =\n resolution.formatter === undefined\n ? {\n configPath,\n inputs: resolution.inputs,\n controlStack: resolution.controlStack,\n artifacts,\n }\n : {\n configPath,\n inputs: resolution.inputs,\n formatter: resolution.formatter,\n controlStack: resolution.controlStack,\n artifacts,\n };\n projects.set(configPath, project);\n return project;\n }\n\n function stopManagingProject(configPath: string): void {\n const hadProject = projects.delete(configPath);\n for (const document of documents.all()) {\n if (documentConfigPaths.get(document.uri) === configPath) {\n documentConfigPaths.delete(document.uri);\n if (hadProject) {\n void connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });\n }\n }\n }\n }\n\n async function republishOpenDocumentsForConfig(configPath: string): Promise<void> {\n for (const document of documents.all()) {\n const knownConfigPath = documentConfigPaths.get(document.uri);\n if (knownConfigPath === configPath) {\n await publish(document.uri, document.getText());\n continue;\n }\n\n const filePath = filePathFromUri(document.uri);\n if (filePath === undefined) {\n continue;\n }\n const nearestConfigPath = await findNearestConfigPathForFile(filePath);\n if (nearestConfigPath === configPath) {\n documentConfigPaths.set(document.uri, configPath);\n await publish(document.uri, document.getText());\n }\n }\n }\n\n function publishSafely(uri: string, text: string): void {\n void publish(uri, text).catch((error: unknown) => {\n connection.console.error(error instanceof Error ? error.message : String(error));\n });\n }\n\n async function formatDocument(uri: string): Promise<TextEdit[]> {\n const document = documents.get(uri);\n if (document === undefined) {\n return [];\n }\n\n let project: ProjectState | undefined;\n try {\n project = await resolveProjectForDocument(uri);\n } catch {\n return [];\n }\n if (project === undefined || !project.inputs.includes(uri)) {\n return [];\n }\n\n const source = document.getText();\n let formatted: string;\n try {\n formatted = format(source, project.formatter);\n } catch {\n return [];\n }\n\n if (formatted === source) {\n return [];\n }\n\n return [\n {\n range: { start: { line: 0, character: 0 }, end: document.positionAt(source.length) },\n newText: formatted,\n },\n ];\n }\n\n connection.onInitialize(async (params): Promise<InitializeResult> => {\n rootPath = resolveRootPath(params.rootUri, params.rootPath);\n watchedConfigGlob = join(rootPath, '**', CONFIG_FILENAME);\n supportsWatchedFilesRegistration = clientSupportsWatchedFilesRegistration(params);\n\n return {\n capabilities: {\n textDocumentSync: TextDocumentSyncKind.Incremental,\n documentFormattingProvider: true,\n },\n };\n });\n\n connection.onInitialized(() => {\n if (supportsWatchedFilesRegistration) {\n void connection\n .sendRequest(RegistrationRequest.type, {\n registrations: [\n {\n id: 'prisma-next-config-watcher',\n method: DidChangeWatchedFilesNotification.type.method,\n registerOptions: { watchers: [{ globPattern: watchedConfigGlob }] },\n },\n ],\n })\n .catch(() => undefined);\n } else {\n connection.console.warn(\n 'Client does not support dynamic file-watcher registration; Prisma Next config changes will not be picked up without a restart.',\n );\n }\n });\n\n connection.onDidChangeWatchedFiles(async (params) => {\n const changedConfigPaths = configPathsFromWatchedChanges(\n params.changes.map((change) => filePathFromUri(change.uri)),\n );\n for (const configPath of changedConfigPaths) {\n try {\n await refreshProject(configPath);\n } catch {\n stopManagingProject(configPath);\n continue;\n }\n await republishOpenDocumentsForConfig(configPath);\n }\n });\n\n connection.onDocumentFormatting((params) => formatDocument(params.textDocument.uri));\n\n documents.onDidOpen((event) => {\n publishSafely(event.document.uri, event.document.getText());\n });\n documents.onDidChangeContent((event) => {\n publishSafely(event.document.uri, event.document.getText());\n });\n documents.onDidClose((event) => {\n const uri = event.document.uri;\n const configPath = documentConfigPaths.get(uri);\n if (configPath !== undefined) {\n projects.get(configPath)?.artifacts.remove(uri);\n }\n documentConfigPaths.delete(uri);\n void connection.sendDiagnostics({ uri, diagnostics: [] });\n });\n\n documents.listen(connection);\n connection.listen();\n\n function artifactsForDocument(uri: string): ProjectArtifacts | undefined {\n const configPath = documentConfigPaths.get(uri);\n return configPath === undefined ? undefined : projects.get(configPath)?.artifacts;\n }\n\n return {\n dispose: () => {\n connection.dispose();\n },\n getDocumentAst: (uri) => artifactsForDocument(uri)?.getDocument(uri),\n getProjectSymbolTable: (uri) => artifactsForDocument(uri)?.getSymbolTable(),\n };\n}\n\nfunction toLspSeverity(severity: number): DiagnosticSeverity {\n switch (severity) {\n case ParseDiagnosticSeverity.Warning:\n return DiagnosticSeverity.Warning;\n case ParseDiagnosticSeverity.Information:\n return DiagnosticSeverity.Information;\n case ParseDiagnosticSeverity.Hint:\n return DiagnosticSeverity.Hint;\n default:\n return DiagnosticSeverity.Error;\n }\n}\n\nfunction clientSupportsWatchedFilesRegistration(params: InitializeParams): boolean {\n return params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration === true;\n}\n\nfunction resolveRootPath(\n rootUri: string | null | undefined,\n rootPath: string | null | undefined,\n): string {\n if (rootUri) {\n return fileURLToPath(rootUri);\n }\n if (rootPath) {\n return rootPath;\n }\n return process.cwd();\n}\n\nfunction filePathFromUri(uri: string): string | undefined {\n try {\n return fileURLToPath(uri);\n } catch {\n return undefined;\n }\n}\n\nfunction configPathsFromWatchedChanges(paths: readonly (string | undefined)[]): Set<string> {\n const configPaths = new Set<string>();\n for (const path of paths) {\n if (path?.endsWith(CONFIG_FILENAME)) {\n configPaths.add(path);\n }\n }\n return configPaths;\n}\n","import { createConnection, ProposedFeatures } from 'vscode-languageserver/node';\nimport { createServer, type LanguageServer } from './server';\n\nexport function startServer(): LanguageServer {\n const connection = createConnection(ProposedFeatures.all);\n return createServer(connection);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@prisma-next/language-server",
3
- "version": "0.14.0-dev.20",
3
+ "version": "0.14.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "description": "Prisma Next language server: PSL diagnostics and whole-document formatting over LSP",
8
8
  "dependencies": {
9
- "@prisma-next/config-loader": "0.14.0-dev.20",
10
- "@prisma-next/errors": "0.14.0-dev.20",
11
- "@prisma-next/framework-components": "0.14.0-dev.20",
12
- "@prisma-next/psl-parser": "0.14.0-dev.20",
13
- "@prisma-next/utils": "0.14.0-dev.20",
9
+ "@prisma-next/config-loader": "0.14.0",
10
+ "@prisma-next/errors": "0.14.0",
11
+ "@prisma-next/framework-components": "0.14.0",
12
+ "@prisma-next/psl-parser": "0.14.0",
13
+ "@prisma-next/utils": "0.14.0",
14
14
  "vscode-languageserver": "10.0.0",
15
15
  "vscode-languageserver-textdocument": "1.0.12"
16
16
  },
17
17
  "devDependencies": {
18
- "@prisma-next/test-utils": "0.14.0-dev.20",
19
- "@prisma-next/tsconfig": "0.14.0-dev.20",
20
- "@prisma-next/tsdown": "0.14.0-dev.20",
18
+ "@prisma-next/test-utils": "0.14.0",
19
+ "@prisma-next/tsconfig": "0.14.0",
20
+ "@prisma-next/tsdown": "0.14.0",
21
21
  "@types/node": "25.9.1",
22
22
  "tsdown": "0.22.1",
23
23
  "typescript": "5.9.3",
package/src/server.ts CHANGED
@@ -8,7 +8,6 @@ import {
8
8
  type Diagnostic,
9
9
  DiagnosticSeverity,
10
10
  DidChangeWatchedFilesNotification,
11
- type FoldingRange,
12
11
  type InitializeParams,
13
12
  type InitializeResult,
14
13
  RegistrationRequest,
@@ -19,7 +18,6 @@ import {
19
18
  import { TextDocument } from 'vscode-languageserver-textdocument';
20
19
  import { CONFIG_FILENAME, resolveConfigInputs } from './config-resolution';
21
20
  import { ParseDiagnosticSeverity } from './diagnostic-mapping';
22
- import { computeFoldingRanges } from './folding-ranges';
23
21
  import type { PipelineInputs } from './pipeline';
24
22
  import {
25
23
  type CachedDocument,
@@ -257,7 +255,6 @@ export function createServer(connection: Connection): LanguageServer {
257
255
  capabilities: {
258
256
  textDocumentSync: TextDocumentSyncKind.Incremental,
259
257
  documentFormattingProvider: true,
260
- foldingRangeProvider: true,
261
258
  },
262
259
  };
263
260
  });
@@ -299,23 +296,6 @@ export function createServer(connection: Connection): LanguageServer {
299
296
 
300
297
  connection.onDocumentFormatting((params) => formatDocument(params.textDocument.uri));
301
298
 
302
- connection.onFoldingRanges(async (params): Promise<FoldingRange[]> => {
303
- let project: ProjectState | undefined;
304
- try {
305
- project = await resolveProjectForDocument(params.textDocument.uri);
306
- } catch {
307
- return [];
308
- }
309
- if (project === undefined) {
310
- return [];
311
- }
312
- const cached = project.artifacts.getDocument(params.textDocument.uri);
313
- if (cached === undefined) {
314
- return [];
315
- }
316
- return computeFoldingRanges(cached.document, cached.sourceFile);
317
- });
318
-
319
299
  documents.onDidOpen((event) => {
320
300
  publishSafely(event.document.uri, event.document.getText());
321
301
  });
@@ -1,70 +0,0 @@
1
- import {
2
- type DocumentAst,
3
- NamespaceDeclarationAst,
4
- type NamespaceMemberAst,
5
- type SourceFile,
6
- type TypesBlockAst,
7
- } from '@prisma-next/psl-parser/syntax';
8
- import { type FoldingRange, FoldingRangeKind } from 'vscode-languageserver';
9
-
10
- type Declaration = NamespaceMemberAst | TypesBlockAst | NamespaceDeclarationAst;
11
-
12
- /**
13
- * Computes folding ranges for block declarations in a PSL document.
14
- *
15
- * Block types that produce folding ranges:
16
- * - model (e.g., `model User { ... }`)
17
- * - composite type (e.g., `type Address { ... }`)
18
- * - namespace (e.g., `namespace billing { ... }`)
19
- * - generic blocks (generator, datasource, extension blocks)
20
- * - types block (e.g., `types { ... }`)
21
- *
22
- * The range spans from the line containing `{` to the line containing `}`.
23
- */
24
- export function computeFoldingRanges(
25
- document: DocumentAst,
26
- sourceFile: SourceFile,
27
- ): FoldingRange[] {
28
- const ranges: FoldingRange[] = [];
29
- collectFoldingRanges(document, sourceFile, ranges);
30
- return ranges;
31
- }
32
-
33
- function collectFoldingRanges(
34
- document: DocumentAst,
35
- sourceFile: SourceFile,
36
- ranges: FoldingRange[],
37
- ): void {
38
- for (const declaration of document.declarations()) {
39
- addFoldingRange(declaration, sourceFile, ranges);
40
-
41
- const namespace = NamespaceDeclarationAst.cast(declaration.syntax);
42
- if (namespace !== undefined) {
43
- for (const nested of namespace.declarations()) {
44
- addFoldingRange(nested, sourceFile, ranges);
45
- }
46
- }
47
- }
48
- }
49
-
50
- function addFoldingRange(
51
- declaration: Declaration,
52
- sourceFile: SourceFile,
53
- ranges: FoldingRange[],
54
- ): void {
55
- const lbrace = declaration.lbrace();
56
- const rbrace = declaration.rbrace();
57
-
58
- if (lbrace === undefined || rbrace === undefined) {
59
- return;
60
- }
61
-
62
- const startLine = sourceFile.positionAt(lbrace.offset).line;
63
- const endLine = sourceFile.positionAt(rbrace.offset).line;
64
-
65
- ranges.push({
66
- startLine,
67
- endLine,
68
- kind: FoldingRangeKind.Region,
69
- });
70
- }