Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
d3fae8d
test(vscode): add real multi-root integration workspace
StevenMcClankerton Aug 22, 2026
997db88
feat(vscode): add document ownership coordinator
StevenMcClankerton Aug 24, 2026
e21d76c
fix(vscode): guard ownership commit effects
StevenMcClankerton Aug 24, 2026
a459ee2
fix(vscode): align ownership after async commits
StevenMcClankerton Aug 24, 2026
bb24c24
feat(vscode): gate bundled client by document owner
StevenMcClankerton Aug 24, 2026
d10f54b
fix(vscode): balance bundled document synchronization
StevenMcClankerton Aug 24, 2026
a75f6bb
fix(vscode): reset bundled state on restart
StevenMcClankerton Aug 24, 2026
bcefc7e
feat(vscode): start root-local Prisma Next clients
StevenMcClankerton Aug 24, 2026
15e27dd
fix(vscode): launch local Prisma CLI with exact args
StevenMcClankerton Aug 24, 2026
5a26332
feat(vscode): transfer Prisma document ownership
StevenMcClankerton Aug 24, 2026
a2a44d2
fix(vscode): cancel routing for closed documents
StevenMcClankerton Aug 24, 2026
038dc7e
test(vscode): prove local Prisma Next routing
StevenMcClankerton Aug 24, 2026
426e6a1
test(vscode): restore integration fixtures
StevenMcClankerton Aug 24, 2026
eee2aa6
test(vscode): make routing e2e setup deterministic
StevenMcClankerton Aug 24, 2026
4d2f4ff
test(vscode): opt into bundled routing readiness
StevenMcClankerton Aug 24, 2026
a1d73fe
fix(vscode): await bundled client before routing
StevenMcClankerton Aug 24, 2026
deba8b4
fix(vscode): stabilize bundled client startup
StevenMcClankerton Aug 24, 2026
379d007
fix(vscode): contain bundled client stop failures
StevenMcClankerton Aug 24, 2026
9c89155
test(vscode): align routing diagnostics with local CLI
StevenMcClankerton Aug 24, 2026
bfb0210
test(vscode): preserve document EOL in routing E2E
StevenMcClankerton Aug 24, 2026
6dee7f7
test(vscode): support renamed macOS executable
StevenMcClankerton Aug 24, 2026
fb1701c
ci: run extension e2e harness on Node 22
StevenMcClankerton Aug 24, 2026
ff84827
fix(vscode): make studio asset copy repeatable
StevenMcClankerton Aug 24, 2026
89f6f7b
test(vscode): isolate Playwright Electron attempts
StevenMcClankerton Aug 24, 2026
cea546b
fix(vscode): shorten Playwright IPC paths on macOS
StevenMcClankerton Aug 24, 2026
91fb3dc
test(vscode): verify side-by-side Prisma completions
StevenMcClankerton Aug 25, 2026
a81ac7e
docs(vscode): document completion routing oracle
StevenMcClankerton Aug 25, 2026
a0a2fb8
test(vscode): load Prisma 8 ORM config
StevenMcClankerton Aug 25, 2026
4af47be
chore(vscode): type-check integration fixture lint
StevenMcClankerton Aug 25, 2026
f3b9117
fix(vscode): register local Prisma providers cross-platform
StevenMcClankerton Aug 25, 2026
d2544a0
fix(vscode): gate local semantic providers by owner
StevenMcClankerton Aug 25, 2026
2603c15
fix(vscode): fork local Prisma language server
StevenMcClankerton Aug 25, 2026
1a9070a
fix(vscode): use fixed Prisma 8 ORM stack
StevenMcClankerton Aug 25, 2026
d0285aa
test(vscode): use coherent Prisma 8 LSP build
StevenMcClankerton Aug 25, 2026
0d18a36
refactor(vscode): clarify language server ownership
StevenMcClankerton Aug 25, 2026
0d21c20
fix(vscode): preserve ownership through restart failures
StevenMcClankerton Aug 25, 2026
04c1c98
fix(vscode): serialize legacy server restarts
StevenMcClankerton Aug 25, 2026
bcaa22a
fix(vscode): serialize language server disposal
StevenMcClankerton Aug 25, 2026
8a7a933
fix(vscode): await plugin deactivation
StevenMcClankerton Aug 25, 2026
907fb2a
docs: simplify Prisma Next routing guidance
StevenMcClankerton Aug 25, 2026
6cde503
refactor(vscode): share document owner equality
StevenMcClankerton Aug 25, 2026
8571037
fix(vscode): clean up failed Prisma Next clients
StevenMcClankerton Aug 25, 2026
5280f73
fix(vscode): escape workspace glob roots
StevenMcClankerton Aug 25, 2026
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
6 changes: 5 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ module.exports = {
{
files: ['*.ts'],
parserOptions: {
project: ['./tsconfig.json', './packages/*/tsconfig.json'],
project: [
'./tsconfig.json',
'./packages/*/tsconfig.json',
'./packages/vscode/tests/fixtures/integration-workspace/root-a/tsconfig.eslint.json',
],
},
extends: [
'plugin:@typescript-eslint/recommended',
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/4_e2e_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: 'pnpm'
- name: Install Dependencies
run: pnpm install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: 'pnpm'
- name: Install Dependencies
run: pnpm install
Expand Down
22 changes: 22 additions & 0 deletions docs/language-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,25 @@ for (const line of schema.iterLines()) {
See [Prisma Multi-File Schema Documentation][multi-file-docs] for details.

[multi-file-docs]: https://www.prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema

## VS Code document routing
Comment thread
SevInf marked this conversation as resolved.

When `prisma.pinToPrisma6` is disabled, the VS Code extension routes each open Prisma document independently:

| Document | Owner |
| --------------------------------------------------------------------------------- | ------------------------------------------ |
| No `// use prisma-next` directive | Legacy language server |
| Directive present, trusted file workspace, matching root, and Prisma Next CLI available | Prisma Next client for that workspace root |
| Directive present but Prisma Next execution is ineligible or unavailable | No active language-server synchronization |

The directive is content-based and applies per file. A marked file does not opt sibling files or the rest of a multi-file schema into Prisma Next tooling.

For marked files, the extension uses only the Prisma CLI installed at:

```text
<workspace-root>/node_modules/prisma/dist/prisma.js
```

The workspace must be trusted. The extension does not invoke a package manager, search parent directories, or fall back to a global installation. If the CLI is unavailable, the marked file has no language-server features until a suitable Prisma Next server can be started.

The extension starts at most one Prisma Next language server per workspace root. Adding or removing the directive in an open file transfers that file between the legacy and Prisma Next servers without requiring a save or restart. Prisma Next servers do not restart automatically after a failure; use **Prisma: Restart Language Server** to retry. Pinning the workspace to Prisma 6 routes every Prisma document to the legacy Prisma 6 server.
18 changes: 16 additions & 2 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ const userFile = helper.file('User.prisma')
pnpm test:e2e # runs scripts/e2e.sh
```

Uses the VS Code test framework for E2E testing of the extension. The language
server is bundled with the extension, so tests always use the local version.
Uses the VS Code test framework for E2E testing of the extension. The legacy
language server ships with the extension, so tests exercise the workspace build.

### Post-Publish E2E Testing

Expand All @@ -74,3 +74,17 @@ pnpm test:e2e:vsix <extension_type> <os> <version>

Both scripts use the same tests in `packages/vscode/src/__test__` with fixtures
located in `packages/vscode/fixtures`.

## VS Code Electron integration tests

The Electron runner opens `packages/vscode/tests/fixtures/integration-workspace.code-workspace`. Its single workspace root is a pnpm importer with the lockfile-resolved `prisma@8.0.0-rc.10-dev.82` CLI at `node_modules/prisma/dist/prisma.js`. The fixture uses `@prisma/cli-engine@0.2.3` and `@prisma/orm-postgres@8.0.0-rc.7-dev.1` (which resolves `@prisma/orm-toolchain@8.0.0-rc.7-dev.1`) plus a valid `prisma.config.ts` whose contract is only `next.prisma`.

Run the focused minimum-runtime workspace suite with:

```bash
pnpm --filter prisma test:integration:workspace
```

This command rebuilds the extension, compiles the integration tests, launches the minimum supported VS Code version, and runs `workspace.test.js`. The test uses the legacy language server and the real Prisma Next server launched from the workspace-local Prisma CLI side by side; no mock language-server executable is part of the fixture.

The test opens an empty, unmarked `legacy.prisma` and a marked `next.prisma` in separate editor columns, then polls only the public `vscode.executeCompletionItemProvider` command with a fixed timeout. At `(0, 0)`, the legacy Prisma 7 server must offer `datasource`, `generator`, and `model`, classify `datasource` as `CompletionItemKind.Class`, and omit `namespace`. At `(1, 0)`, the Prisma Next server from the workspace-local Prisma 8 CLI must offer `namespace` as `CompletionItemKind.Keyword` with detail `PSL declaration keyword`, and omit `datasource`. These assertions verify observable routing behavior without extension-private commands, owner state, events, or process start counts.
2 changes: 1 addition & 1 deletion packages/vscode/.vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fixtures/**

# Keep these files (not ignored):
# - dist/extension.js (bundled extension)
# - dist/language-server/** (bundled language server)
# - dist/language-server/** (legacy language server)
# - dist/node_modules/** (copied static assets)
# - syntaxes/** (TextMate grammars)
# - language-configuration.json
Expand Down
13 changes: 7 additions & 6 deletions packages/vscode/esbuild.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const languageServerConfig = {

/**
* Configuration for the Prisma 6 Language Server.
* This is bundled separately and used when pinToPrisma6 is enabled.
* This legacy server is built separately and used when pinToPrisma6 is enabled.
* @type {import('esbuild').BuildOptions}
*/
const prisma6LanguageServerConfig = {
Expand Down Expand Up @@ -258,12 +258,13 @@ function copyStaticAssets() {
}

console.log('Copying @prisma/studio-core-licensed static assets...')
// Use dereference to resolve symlinks (important for pnpm)
// Replace the previous copy so repeated builds work with pnpm's symlinked package directory.
rmSync(studioDest, { recursive: true, force: true })
cpSync(studioSrc, studioDest, { recursive: true, dereference: true })

// Copy prisma-schema-wasm WASM file to Prisma 6 language server directory
// The WASM is loaded via __dirname in the bundled code, so it needs to be
// in the same directory as the bundled Prisma 6 language server bin.js
// The WASM is loaded via __dirname in the legacy server code, so it needs to be
// in the same directory as the legacy Prisma 6 language server bin.js.
// Note: We need to find the Prisma 6 version specifically since there are
// two versions (Prisma 6 and Prisma 7) of @prisma/prisma-schema-wasm
const prisma6LsDistDir = join(__dirname, 'dist/prisma6-language-server')
Expand Down Expand Up @@ -291,8 +292,8 @@ function copyStaticAssets() {
cpSync(prisma6WasmSrc, join(prisma6LsDistDir, 'prisma_schema_build_bg.wasm'))

// Copy prisma-schema-wasm WASM file to language server directory
// The WASM is loaded via __dirname in the bundled code, so it needs to be
// in the same directory as the bundled language server bin.js
// The WASM is loaded via __dirname in the legacy server code, so it needs to be
// in the same directory as the legacy language server bin.js.
const lsDistDir = join(__dirname, 'dist/language-server')
mkdirSync(lsDistDir, { recursive: true })

Expand Down
12 changes: 7 additions & 5 deletions packages/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"license": "Apache-2.0",
"icon": "logo_white.png",
"engines": {
"vscode": "^1.96.0"
"vscode": "^1.104.0"
},
"publisher": "Prisma",
"categories": [
Expand Down Expand Up @@ -46,6 +46,7 @@
"build:types": "tsc -p ./ --emitDeclarationOnly",
"watch": "node esbuild.mjs --watch",
"test:integration": "rm -rf ./dist-tests && node esbuild.mjs && tsc -p tsconfig.test.json && node dist-tests/__test__/runTest true",
"test:integration:workspace": "rm -rf ./dist-tests && node esbuild.mjs && tsc -p tsconfig.test.json && node dist-tests/__test__/runTest --minimum-only --test-pattern workspace.test.js",
"test:playwright": "playwright test",
"test:playwright:headless": "CI=true xvfb-run -a npm run test:playwright",
"vscode:prepublish": "pnpm run build",
Expand Down Expand Up @@ -686,8 +687,8 @@
"@types/mocha": "10.0.10",
"@types/node": "20.14.8",
"@types/sinon": "^20.0.0",
"@types/vscode": "1.96.0",
"@vscode/test-electron": "2.4.1",
"@types/vscode": "1.104.0",
"@vscode/test-electron": "3.1.0",
"@vscode/vsce": "2.29.0",
"esbuild": "^0.27.1",
"glob": "8.1.0",
Expand All @@ -696,11 +697,12 @@
"ovsx": "0.10.1",
"pkg-types": "2.2.0",
"sinon": "^21.0.0",
"typescript": "5.7.3"
"typescript": "5.7.3",
"vitest": "^2.1.0"
},
"gitHead": "7d51b157647fe1705813a30d1a77b8ccf136b8d4",
"publishConfig": {
"access": "public"
},
"preview": false
}
}
1 change: 1 addition & 0 deletions packages/vscode/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
testDir: './tests/playwright',
testIgnore: '**/*.unit.test.ts',
timeout: process.env.CI ? 120000 : 60000, // Longer timeout for CI
fullyParallel: false,
forbidOnly: !!process.env.CI,
Expand Down
20 changes: 19 additions & 1 deletion packages/vscode/src/__test__/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ export async function activate(docUri: vscode.Uri): Promise<void> {
try {
doc = await vscode.workspace.openTextDocument(docUri)
editor = await vscode.window.showTextDocument(doc)
await sleep(2500) // Wait for server activation
} catch (e) {
console.error(e)
return
}

await sleep(2500) // Wait for server activation
}

export function toRange(sLine: number, sChar: number, eLine: number, eChar: number): vscode.Range {
Expand All @@ -49,6 +51,22 @@ export const getDocUri = (p: string): vscode.Uri => {
return vscode.Uri.file(getDocPath(p))
}

export function getWorkspaceFolder(name: string): vscode.WorkspaceFolder {
const workspaceFolder = vscode.workspace.workspaceFolders?.find((folder) => folder.name === name)
if (!workspaceFolder) {
throw new Error(`Workspace folder not found: ${name}`)
}
return workspaceFolder
}

export function getWorkspaceDocUri(workspaceFolder: vscode.WorkspaceFolder, relativePath: string): vscode.Uri {
return vscode.Uri.joinPath(workspaceFolder.uri, relativePath)
}

export function getPrismaCliEntrypoint(workspaceFolder: vscode.WorkspaceFolder): vscode.Uri {
return vscode.Uri.joinPath(workspaceFolder.uri, 'node_modules', 'prisma', 'dist', 'prisma.js')
}

export async function setTestContent(content: string): Promise<boolean> {
const all = new vscode.Range(doc.positionAt(0), doc.positionAt(doc.getText().length))
return editor.edit((eb) => eb.replace(all, content))
Expand Down
3 changes: 2 additions & 1 deletion packages/vscode/src/__test__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export function run(): Promise<void> {
const testsRoot = __dirname

return new Promise((resolve, reject) => {
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
const testPattern = process.env.VSCODE_TEST_PATTERN ?? '**/**.test.js'
glob(testPattern, { cwd: testsRoot }, (err, files) => {
if (err) {
return reject(err)
}
Expand Down
15 changes: 13 additions & 2 deletions packages/vscode/src/__test__/language-server/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
# Integration tests for the Language Server

Only one test per feature is done here.
The goal is to check that the integration is working between the VS Code extension and the Language Server.
Only one test per feature is done here. The goal is to check that the integration is working between the VS Code extension and the Language Server.

The integration runner opens `tests/fixtures/integration-workspace.code-workspace`. Its single root is a pnpm workspace importer containing the lockfile-resolved real `prisma@8.0.0-rc.10-dev.82` CLI with `@prisma/cli-engine@0.2.3` and `@prisma/orm-postgres@8.0.0-rc.7-dev.1` (resolving `@prisma/orm-toolchain@8.0.0-rc.7-dev.1`), plus a valid `prisma.config.ts` whose contract is only the marked `next.prisma` fixture.

Run the full minimum-and-latest integration suite with `pnpm test:integration`. Run the focused side-by-side completion suite on the minimum supported VS Code runtime with:

```bash
pnpm --filter prisma test:integration:workspace
```

The focused suite opens an empty, unmarked `legacy.prisma` and a marked `next.prisma` in separate editor columns. It polls only the public `vscode.executeCompletionItemProvider` command with a fixed timeout. The legacy Prisma 7 language server must offer `datasource`, `generator`, and `model`, classify `datasource` as `CompletionItemKind.Class`, and omit `namespace`. The real Prisma Next server launched from the workspace-local Prisma 8 CLI must offer `namespace` as `CompletionItemKind.Keyword` with detail `PSL declaration keyword`, and omit `datasource`.

No mock language server or extension-private routing state is used. The test asserts observable editor behavior rather than owners, routing events, document synchronization bookkeeping, or process start counts.
22 changes: 18 additions & 4 deletions packages/vscode/src/__test__/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { runTests } from '@vscode/test-electron'
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
const packageJson = require('../../package.json') as { engines: { vscode: string } }

function test(version?: string) {
function test(version?: string, testPattern?: string) {
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../../')
Expand All @@ -14,12 +14,17 @@ function test(version?: string) {
// Passed to --extensionTestsPath
const extensionTestsPath = path.resolve(__dirname, './index')

// The explicit multi-root workspace opened by every integration test run.
const workspacePath = path.resolve(__dirname, '../../tests/fixtures/integration-workspace.code-workspace')

// Downloads VS Code, unzip it and run the integration test
return runTests({
version, // optional, default = latest
extensionDevelopmentPath,
extensionTestsPath,
extensionTestsEnv: testPattern ? { VSCODE_TEST_PATTERN: testPattern } : undefined,
launchArgs: [
workspacePath,
// This disables all extensions except the one being testing
'--disable-extensions',
// ? This may or may not be necessary?
Expand All @@ -41,15 +46,24 @@ function test(version?: string) {

async function main(): Promise<void> {
try {
const minimumOnly = process.argv.includes('--minimum-only')
const testPatternFlag = process.argv.indexOf('--test-pattern')
const testPattern = testPatternFlag === -1 ? undefined : process.argv[testPatternFlag + 1]
if (testPatternFlag !== -1 && !testPattern) {
throw new Error('--test-pattern requires a glob pattern')
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

// 1 - Run on our minimum supported version from package.json
// eslint-disable-next-line
const minimumSupportedVersion: string = packageJson.engines.vscode.replace('~', '').replace('^', '') // remove semver chars
console.log(`*** Testing on minimum supported version of VS Code: ${minimumSupportedVersion} ***`)
await test(minimumSupportedVersion)
await test(minimumSupportedVersion, testPattern)

// 2 - Run again on latest version
console.log(`*** Testing on latest version of VS Code ***`)
await test()
if (!minimumOnly) {
console.log(`*** Testing on latest version of VS Code ***`)
await test(undefined, testPattern)
}
} catch (err) {
const errMsg = err instanceof Error ? ` ${err.message}` : ''

Expand Down
Loading
Loading