vscode-apollo 2.3.6 → 2.5.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.
@@ -7,26 +7,63 @@ on:
7
7
  jobs:
8
8
  test:
9
9
  name: Run E2E tests
10
- runs-on: ubuntu-latest
10
+ runs-on: "${{ matrix.os }}"
11
11
  strategy:
12
12
  matrix:
13
13
  version: ["1.90.0", "stable", "insiders"]
14
+ os: [ubuntu-latest]
15
+ include:
16
+ - version: "stable"
17
+ os: "windows-latest"
14
18
  steps:
15
- - run: sudo apt update && sudo apt install -y libasound2 libgbm1 libgtk-3-0 libnss3 xvfb expect
19
+ - run: sudo apt update && sudo apt install -y libasound2t64 libgbm1 libgtk-3-0 libnss3 xvfb expect
20
+ if: runner.os == 'Linux'
16
21
  - uses: actions/checkout@v4
17
22
  - uses: actions/setup-node@v4
18
23
  with:
19
24
  cache: "npm"
20
25
  - run: npm install
21
- - run: npm run build:production
22
26
  - run: echo 'APOLLO_KEY="service:bob-123:489fhseo4"' > ./sampleWorkspace/spotifyGraph/.env
23
- - run: |
27
+ shell: bash
28
+ - name: Install & Configure Rover (Linux)
29
+ run: |
24
30
  expect <<EOF
25
31
  spawn ./node_modules/.bin/rover config auth --profile VSCode-E2E
26
32
  expect "Copy the key and paste it into the prompt below."
27
33
  send -- "test\n"
28
34
  expect eof
29
35
  EOF
30
- - run: xvfb-run -a npm run test:extension
36
+ if: runner.os == 'Linux'
37
+ - name: Install Rover (Windows)
38
+ run: ./node_modules/.bin/rover.cmd --version
39
+ if: runner.os == 'Windows'
40
+ - name: Configure Rover (Windows)
41
+ run: |
42
+ [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
43
+ Start-Process -FilePath ./node_modules/.bin/rover.cmd -ArgumentList "config","auth","--profile","VSCode-E2E"
44
+
45
+ Start-Sleep -m 1000
46
+
47
+ [System.Windows.Forms.SendKeys]::SendWait("test")
48
+ [System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
49
+ if: runner.os == 'Windows'
50
+ shell: powershell
51
+ - name: Adjust configuration (Windows)
52
+ run: |
53
+ sed -i -e 's/\(bin:.*\)/\1.exe/' sampleWorkspace/rover/apollo.config.yaml
54
+ cat sampleWorkspace/rover/apollo.config.yaml
55
+ # for some reason, windows seems to ignore the jest.e2e.config.js file
56
+ echo "module.exports = require('./jest.e2e.config')" > jest.config.ts
57
+ shell: bash
58
+ if: runner.os == 'Windows'
59
+ - run: npm run build:production
60
+ - name: "Run Extension E2E tests (Linux)"
61
+ run: xvfb-run -a npm run test:extension
62
+ env:
63
+ VSCODE_VERSION: "${{ matrix.version }}"
64
+ if: runner.os == 'Linux'
65
+ - name: "Run Extension E2E tests (Windows)"
66
+ run: npm run test:extension
31
67
  env:
32
68
  VSCODE_VERSION: "${{ matrix.version }}"
69
+ if: runner.os == 'Windows'
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- v20
1
+ 22
package/.vscodeignore CHANGED
@@ -25,3 +25,6 @@ sampleWorkspace
25
25
  renovate.json
26
26
  images/**/*.gif
27
27
  images/marketplace
28
+ .yalc
29
+ yalc.lock
30
+ start-ac.mjs
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 2.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#188](https://github.com/apollographql/vscode-graphql/pull/188) [`595784f0`](https://github.com/apollographql/vscode-graphql/commit/595784f057de18d987768fa1aaa8c37a5fa802c7) Thanks [@phryneas](https://github.com/phryneas)! - Adds experimental integration of the Apollo Client DevTools
8
+
9
+ ## 2.4.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#236](https://github.com/apollographql/vscode-graphql/pull/236) [`9a4403db`](https://github.com/apollographql/vscode-graphql/commit/9a4403db7b8f9cb03638d83a7e3c29dd3c3077b7) Thanks [@pubmodmatt](https://github.com/pubmodmatt)! - Add support for GraphQL in Notebook cells
14
+
15
+ ### Patch Changes
16
+
17
+ - [#240](https://github.com/apollographql/vscode-graphql/pull/240) [`3dfd9ac0`](https://github.com/apollographql/vscode-graphql/commit/3dfd9ac08a20ff92cafb398f1a8c71e5f7d03519) Thanks [@phryneas](https://github.com/phryneas)! - Fix a bug handling windows path separators in `FileSet` instances.
18
+
3
19
  ## 2.3.6
4
20
 
5
21
  ### Patch Changes
@@ -0,0 +1,16 @@
1
+ <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
2
+ <style>
3
+ g {
4
+ fill: black;
5
+ }
6
+ @media (prefers-color-scheme: dark) {
7
+ g {
8
+ fill: white;
9
+ }
10
+ }
11
+ </style>
12
+ <g>
13
+ <polygon points="112.246 49 88.003 49 53 139.848 74.927 139.848 80.648 124.455 113.724 124.455 107.735 107.431 85.961 107.431 100.125 68.34 125.324 139.848 147.252 139.848"></polygon>
14
+ <path d="M196.313,73.038 C196.254,72.728 196.17,72.428 196.057,72.141 C196.03,72.041 195.937,71.856 195.937,71.856 C195.148,70.138 193.414,68.942 191.399,68.942 C188.637,68.942 186.399,71.181 186.399,73.942 C186.399,74.504 186.497,75.042 186.667,75.545 L186.653,75.55 C188.859,83.407 189.999,91.615 189.999,100.001 C189.999,124.04 180.637,146.642 163.64,163.641 C146.641,180.641 124.04,190.001 100,190.001 C75.961,190.001 53.358,180.639 36.361,163.641 C19.36,146.642 10,124.04 10,100.001 C10,75.961 19.362,53.36 36.361,36.361 C53.358,19.361 75.961,10.001 100,10.001 C121.466,10.001 141.781,17.471 157.987,31.174 C157.443,32.56 157.139,34.068 157.139,35.648 C157.139,42.399 162.611,47.869 169.363,47.869 C176.113,47.869 181.586,42.399 181.586,35.648 C181.586,28.897 176.113,23.424 169.363,23.424 C167.89,23.424 166.479,23.684 165.171,24.161 C147.666,9.107 124.9,0 100,0 C44.771,0 0,44.772 0,100.001 C0,155.23 44.771,200.002 100,200.002 C155.229,200.002 200,155.231 200,100.001 C200,90.659 198.709,81.617 196.313,73.038 Z"></path>
15
+ </g>
16
+ </svg>
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "vscode-apollo",
3
3
  "displayName": "Apollo GraphQL",
4
4
  "description": "Rich editor support for GraphQL client and server development that seamlessly integrates with the Apollo platform",
5
- "version": "2.3.6",
5
+ "version": "2.5.0",
6
6
  "referenceID": "87197759-7617-40d0-b32e-46d378e907c7",
7
7
  "author": "Apollo GraphQL <opensource@apollographql.com>",
8
8
  "license": "MIT",
@@ -37,7 +37,8 @@
37
37
  "vscode": "^1.90.0"
38
38
  },
39
39
  "dependencies": {
40
- "@apollo/client": "3.11.8",
40
+ "@apollo/client": "3.12.2",
41
+ "@apollo/client-devtools-vscode": "^4.19.0",
41
42
  "@apollo/subgraph": "2.9.1",
42
43
  "@graphql-tools/schema": "10.0.6",
43
44
  "@wry/equality": "0.5.7",
@@ -62,6 +63,7 @@
62
63
  "vscode-languageserver-textdocument": "1.0.12",
63
64
  "vscode-uri": "3.0.8",
64
65
  "which": "4.0.0",
66
+ "ws": "8.18.0",
65
67
  "zod": "3.23.8",
66
68
  "zod-validation-error": "3.4.0"
67
69
  },
@@ -138,6 +140,23 @@
138
140
  "default": true,
139
141
  "markdownDescription": "Show a \"Run in Studio\" button to the right of Operation Signatures.",
140
142
  "scope": "window"
143
+ },
144
+ "apollographql.devTools.showPanel": {
145
+ "type": "string",
146
+ "enum": [
147
+ "always",
148
+ "never",
149
+ "detect"
150
+ ],
151
+ "default": "never",
152
+ "markdownDescription": "[Experimental Feature] If the Apollo Client DevTools panel should be shown. If set to `detect`, the panel will only be shown if a configuration file with a client project is found in the workspace.",
153
+ "scope": "window"
154
+ },
155
+ "apollographql.devTools.serverPort": {
156
+ "type": "number",
157
+ "default": 7095,
158
+ "markdownDescription": "The Apollo Client DevTools server port. The server will be started as soon as you start using the DevTools panels.",
159
+ "scope": "window"
141
160
  }
142
161
  }
143
162
  },
@@ -279,8 +298,40 @@
279
298
  "command": "apollographql/showStats",
280
299
  "title": "Show Status",
281
300
  "category": "Apollo"
301
+ },
302
+ {
303
+ "command": "apollographql/startDevToolsServer",
304
+ "title": "Start Apollo Client DevTools Server",
305
+ "category": "Apollo",
306
+ "when": "config.apollographql.devTools.showPanel=='always' || (config.apollographql.devTools.showPanel=='detect' && vscode-apollo.hasClientProject)"
307
+ },
308
+ {
309
+ "command": "apollographql/stopDevToolsServer",
310
+ "title": "Stop Apollo Client DevTools Server",
311
+ "category": "Apollo",
312
+ "when": "config.apollographql.devTools.showPanel=='always' || (config.apollographql.devTools.showPanel=='detect' && vscode-apollo.hasClientProject)"
282
313
  }
283
- ]
314
+ ],
315
+ "viewsContainers": {
316
+ "panel": [
317
+ {
318
+ "id": "client-devtools",
319
+ "title": "Apollo Client DevTools",
320
+ "icon": "images/apollo.svg"
321
+ }
322
+ ]
323
+ },
324
+ "views": {
325
+ "client-devtools": [
326
+ {
327
+ "type": "webview",
328
+ "id": "vscode-apollo-client-devtools",
329
+ "name": "Apollo Client DevTools",
330
+ "icon": "images/apollo.svg",
331
+ "when": "config.apollographql.devTools.showPanel=='always' || (config.apollographql.devTools.showPanel=='detect' && vscode-apollo.hasClientProject)"
332
+ }
333
+ ]
334
+ }
284
335
  },
285
336
  "galleryBanner": {
286
337
  "color": "#1d127d",
@@ -25,5 +25,8 @@
25
25
  "path": "../src/language-server/__tests__/fixtures/documents"
26
26
  }
27
27
  ],
28
- "settings": {}
28
+ "settings": {
29
+ "apollographql.devTools.showPanel": "detect",
30
+ "apollographql.devTools.serverPort": 7095
31
+ }
29
32
  }
package/src/build.js CHANGED
@@ -11,6 +11,10 @@ async function main() {
11
11
  entryPoints: [
12
12
  "src/extension.ts",
13
13
  "src/language-server/server.ts",
14
+ {
15
+ in: require.resolve("@apollo/client-devtools-vscode/panel"),
16
+ out: "panel",
17
+ },
14
18
  "src/language-server/config/config.ts",
15
19
  "src/language-server/config/cache-busting-resolver.js",
16
20
  ],
package/src/debug.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { OutputChannel } from "vscode";
2
+ import { TraceValues } from "vscode-languageclient";
3
+ import { format } from "node:util";
2
4
 
3
5
  /**
4
6
  * for errors (and other logs in debug mode) we want to print
@@ -21,6 +23,21 @@ export class Debug {
21
23
  this.outputConsole = outputConsole;
22
24
  }
23
25
 
26
+ private static _traceLevel: Exclude<TraceValues, "compact"> = "off";
27
+ public static get traceLevel(): TraceValues {
28
+ return Debug._traceLevel;
29
+ }
30
+ public static set traceLevel(value: TraceValues | undefined) {
31
+ console.log("setting trace level to", value);
32
+ if (value === "compact") {
33
+ // we do not handle "compact" and it's not possible to set in settings, but it doesn't hurt to at least map
34
+ // it to another value
35
+ this._traceLevel = "messages";
36
+ } else {
37
+ this._traceLevel = value || "off";
38
+ }
39
+ }
40
+
24
41
  /**
25
42
  * Displays an info message prefixed with [INFO]
26
43
  */
@@ -55,6 +72,28 @@ export class Debug {
55
72
  this.outputConsole.appendLine(`[WARN] ${message}`);
56
73
  }
57
74
 
75
+ public static traceMessage(
76
+ short: string,
77
+ verbose = short,
78
+ ...verboseParams: any[]
79
+ ) {
80
+ if (!this.outputConsole) return;
81
+ if (Debug.traceLevel === "verbose") {
82
+ this.outputConsole.appendLine(
83
+ `[Trace] ${format(verbose, ...verboseParams)}`,
84
+ );
85
+ } else if (Debug.traceLevel === "messages") {
86
+ this.outputConsole.appendLine(`[Trace] ${short}`);
87
+ }
88
+ }
89
+
90
+ public static traceVerbose(message: string, ...params: any[]) {
91
+ if (!this.outputConsole) return;
92
+ if (Debug.traceLevel === "verbose") {
93
+ this.outputConsole.appendLine(`[Trace] ${format(message, ...params)}`);
94
+ }
95
+ }
96
+
58
97
  /**
59
98
  * TODO: enable error reporting and telemetry
60
99
  */
@@ -0,0 +1,182 @@
1
+ import * as vscode from "vscode";
2
+ import { devtoolsEvents, sendToDevTools, serverState } from "./server";
3
+
4
+ type ActorMessage = { type: "actor"; message: unknown };
5
+
6
+ export function isActorMessage(message: any): message is ActorMessage {
7
+ return message && message.type === "actor";
8
+ }
9
+
10
+ type DevToolsOpenCommandMessage = {
11
+ type: "vscode:executeCommand";
12
+ command: string;
13
+ arguments?: unknown[];
14
+ };
15
+
16
+ export function isDevToolsExecuteCommandMessage(
17
+ message: any,
18
+ ): message is DevToolsOpenCommandMessage {
19
+ return message && message.type === "vscode:executeCommand";
20
+ }
21
+
22
+ type DevToolsOpenExternalMessage = {
23
+ type: "vscode:openExternal";
24
+ uri: string;
25
+ };
26
+
27
+ export function isDevToolsOpenExternalMessage(
28
+ message: any,
29
+ ): message is DevToolsOpenExternalMessage {
30
+ return message && message.type === "vscode:openExternal";
31
+ }
32
+
33
+ export class DevToolsViewProvider implements vscode.WebviewViewProvider {
34
+ public static readonly viewType = "vscode-apollo-client-devtools";
35
+
36
+ constructor(private readonly _extensionUri: vscode.Uri) {}
37
+
38
+ async resolveWebviewView(
39
+ panel: vscode.WebviewView,
40
+ context: vscode.WebviewViewResolveContext,
41
+ token: vscode.CancellationToken,
42
+ ): Promise<void> {
43
+ vscode.commands.executeCommand("apollographql/startDevToolsServer");
44
+ panel.webview.options = {
45
+ enableScripts: true,
46
+ localResourceRoots: [this._extensionUri],
47
+ };
48
+ panel.webview.html = DevToolsViewProvider._getHtmlForWebview(
49
+ panel.webview,
50
+ this._extensionUri,
51
+ );
52
+ const panelDisposables: vscode.Disposable[] = [];
53
+ panel.webview.onDidReceiveMessage(
54
+ (data) => {
55
+ if (data.source === "apollo-client-devtools") {
56
+ devtoolsEvents.emit("fromDevTools", data);
57
+ }
58
+ if (data.source === "vscode-panel") {
59
+ if (data.type === "mounted") {
60
+ sendToDevTools({
61
+ type: "initializePanel",
62
+ initialContext: {
63
+ port:
64
+ serverState?.port ||
65
+ vscode.workspace
66
+ .getConfiguration("apollographql")
67
+ .get("devTools.serverPort", 0),
68
+ listening: !!serverState?.port,
69
+ },
70
+ });
71
+ }
72
+ }
73
+ },
74
+ this,
75
+ panelDisposables,
76
+ );
77
+ function forwardToDevTools(data: unknown) {
78
+ panel.webview.postMessage(data);
79
+ }
80
+ devtoolsEvents.addListener("toDevTools", forwardToDevTools);
81
+ panel.onDidDispose(() => {
82
+ devtoolsEvents.removeListener("toDevTools", forwardToDevTools);
83
+ for (const disposable of panelDisposables) {
84
+ disposable.dispose();
85
+ }
86
+ });
87
+ }
88
+
89
+ private static _getHtmlForWebview(
90
+ webview: vscode.Webview,
91
+ extensionUri: vscode.Uri,
92
+ ) {
93
+ // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview.
94
+ const scriptUri = webview.asWebviewUri(
95
+ vscode.Uri.joinPath(extensionUri, "lib", "panel.js"),
96
+ );
97
+
98
+ // Use a nonce to only allow a specific script to be run.
99
+ const nonce = getNonce();
100
+
101
+ return `
102
+ <!doctype html>
103
+ <html lang="en">
104
+ <head>
105
+ <meta charset="utf-8" />
106
+
107
+ <!--
108
+ Use a content security policy to only allow loading images from https or from our extension directory,
109
+ and only allow scripts that have a specific nonce.
110
+ -->
111
+ <meta http-equiv="Content-Security-Policy" content="
112
+ default-src 'none';
113
+ style-src ${webview.cspSource} https://fonts.googleapis.com 'unsafe-inline';
114
+ font-src ${webview.cspSource} https://fonts.gstatic.com;
115
+ img-src ${webview.cspSource} https:;
116
+ connect-src ${webview.cspSource} https://*.github.com;
117
+ script-src 'nonce-${nonce}';
118
+ frame-src https://*.apollographql.com/;
119
+ ">
120
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
121
+
122
+ <title>Apollo Client DevTools</title>
123
+ <style>
124
+ html,
125
+ body {
126
+ overflow: hidden;
127
+ font-size: var(--vscode-font-size);
128
+ }
129
+ ::-webkit-scrollbar {
130
+ display: none;
131
+ }
132
+ #devtools {
133
+ width: 100vw;
134
+ height: 100vh;
135
+ }
136
+ </style>
137
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
138
+ <link
139
+ href="https://fonts.googleapis.com/css2?family=Fira+Code&family=Inter:wght@400;500;600;700&display=swap"
140
+ rel="stylesheet"
141
+ />
142
+ </head>
143
+ <body class="text-primary dark:text-primary-dark">
144
+ <div id="devtools"></div>
145
+ <script nonce="${nonce}">
146
+ const vscode = acquireVsCodeApi();
147
+ try {
148
+ // remove VSCode default styles
149
+ _defaultStyles.remove();
150
+ } catch {}
151
+ window.originalPostMessage = window.postMessage;
152
+ window.postMessage = function wrapPostMessage (...args) {
153
+ if (args.length>1 && args[1].startsWith("vscode-webview://")) {
154
+ return window.originalPostMessage(...args);
155
+ }
156
+ return vscode.postMessage(...args);
157
+ };
158
+
159
+ // window.addEventListener("message", (event) => { console.debug(event); });
160
+ </script>
161
+ <script nonce="${nonce}" src="${scriptUri}"></script>
162
+ <script nonce="${nonce}">
163
+ window.postMessage({
164
+ source: "vscode-panel",
165
+ type: "mounted",
166
+ });
167
+ </script>
168
+ </body>
169
+ </html>
170
+ `;
171
+ }
172
+ }
173
+
174
+ function getNonce() {
175
+ let text = "";
176
+ const possible =
177
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
178
+ for (let i = 0; i < 32; i++) {
179
+ text += possible.charAt(Math.floor(Math.random() * possible.length));
180
+ }
181
+ return text;
182
+ }
@@ -0,0 +1,86 @@
1
+ import { WebSocketServer } from "ws";
2
+ import { Disposable } from "vscode";
3
+ import { runServer } from "@apollo/client-devtools-vscode/vscode-server";
4
+ import { Debug } from "../debug";
5
+ import { EventEmitter } from "node:events";
6
+
7
+ export const devtoolsEvents = new EventEmitter<{
8
+ toDevTools: [unknown];
9
+ fromDevTools: [unknown];
10
+ }>();
11
+ devtoolsEvents.addListener("toDevTools", (msg) => {
12
+ Debug.traceMessage(
13
+ `WS > DevTools: ${
14
+ msg && typeof msg === "object" && "type" in msg ? msg.type : "unknown"
15
+ }`,
16
+ "WS > DevTools: %o",
17
+ msg,
18
+ );
19
+ });
20
+ devtoolsEvents.addListener("fromDevTools", (msg) => {
21
+ Debug.traceMessage(
22
+ `DevTools > WS: ${
23
+ msg && typeof msg === "object" && "type" in msg ? msg.type : "unknown"
24
+ }`,
25
+ "DevTools > WS: %o",
26
+ msg,
27
+ );
28
+ });
29
+
30
+ let id = 1;
31
+
32
+ export function sendToDevTools(message: unknown) {
33
+ devtoolsEvents.emit("toDevTools", {
34
+ id: `vscode-${id++}`,
35
+ source: "apollo-client-devtools",
36
+ type: "actor",
37
+ message,
38
+ });
39
+ }
40
+
41
+ export let serverState:
42
+ | { port: false | number; disposable: Disposable }
43
+ | undefined = undefined;
44
+
45
+ export function startServer(port: number) {
46
+ const state = {
47
+ port: false as false | number,
48
+ disposable: new Disposable(() => {
49
+ if (wss) {
50
+ wss.close();
51
+ wss = null;
52
+ }
53
+ if (serverState === state) {
54
+ serverState = undefined;
55
+ }
56
+ sendToDevTools({ type: "port.changed", port, listening: false });
57
+ }),
58
+ };
59
+
60
+ if (serverState?.port === port) {
61
+ // nothing to do
62
+ return;
63
+ }
64
+ // changing port, stop the old server
65
+ serverState?.disposable.dispose();
66
+ serverState = state;
67
+ let wss: WebSocketServer | null = new WebSocketServer({ port });
68
+ wss.on("listening", () => {
69
+ state.port = port;
70
+ sendToDevTools({ type: "port.changed", port, listening: true });
71
+ });
72
+ wss.on("close", () => {
73
+ state.disposable.dispose();
74
+ });
75
+ runServer(wss, {
76
+ addListener: (listener) => {
77
+ devtoolsEvents.addListener("fromDevTools", listener);
78
+ return () => {
79
+ devtoolsEvents.removeListener("fromDevTools", listener);
80
+ };
81
+ },
82
+ postMessage: (message) => {
83
+ devtoolsEvents.emit("toDevTools", message);
84
+ },
85
+ });
86
+ }
package/src/extension.ts CHANGED
@@ -8,10 +8,10 @@ import {
8
8
  DecorationOptions,
9
9
  commands,
10
10
  QuickPickItem,
11
- Disposable,
12
11
  OutputChannel,
13
12
  MarkdownString,
14
13
  Range,
14
+ env,
15
15
  } from "vscode";
16
16
  import StatusBar from "./statusBar";
17
17
  import { getLanguageServerClient } from "./languageServerClient";
@@ -27,6 +27,13 @@ import {
27
27
  printStatsToClientOutputChannel,
28
28
  } from "./utils";
29
29
  import { Debug } from "./debug";
30
+ import {
31
+ DevToolsViewProvider,
32
+ isActorMessage,
33
+ isDevToolsExecuteCommandMessage,
34
+ isDevToolsOpenExternalMessage,
35
+ } from "./devtools/DevToolsViewProvider";
36
+ import { devtoolsEvents, serverState, startServer } from "./devtools/server";
30
37
 
31
38
  const { version } = require("../package.json");
32
39
 
@@ -157,6 +164,17 @@ export async function activate(
157
164
  } else {
158
165
  statusBar.showLoadedState({ hasActiveTextEditor });
159
166
  }
167
+
168
+ const containsClientConfig = response.some(
169
+ (item) => item && !isError(item) && "client" in item,
170
+ );
171
+ if (containsClientConfig) {
172
+ commands.executeCommand(
173
+ "setContext",
174
+ "vscode-apollo.hasClientProject",
175
+ true,
176
+ );
177
+ }
160
178
  } else {
161
179
  Debug.error(
162
180
  `Invalid response type in message apollographql/configFilesFound:\n${JSON.stringify(
@@ -329,6 +347,67 @@ export async function activate(
329
347
  },
330
348
  });
331
349
 
350
+ const provider = new DevToolsViewProvider(context.extensionUri);
351
+ context.subscriptions.push(
352
+ window.registerWebviewViewProvider(DevToolsViewProvider.viewType, provider),
353
+ );
354
+
355
+ function devToolsEventListener(event: unknown) {
356
+ if (!isActorMessage(event)) return;
357
+ const message = event.message;
358
+ if (isDevToolsExecuteCommandMessage(message)) {
359
+ commands.executeCommand(message.command, ...(message.arguments || []));
360
+ }
361
+ if (isDevToolsOpenExternalMessage(message)) {
362
+ env.openExternal(
363
+ // if we `Uri.parse` here, we end up with something that somehow double-encodes some things like `#`
364
+ // interestingly enough, the implementation of `openExternal` also allows for strings to be passed in
365
+ // directly, and that works - so we just pass in the string directly
366
+ message.uri as any as Uri,
367
+ );
368
+ }
369
+ }
370
+ devtoolsEvents.addListener("fromDevTools", devToolsEventListener);
371
+ context.subscriptions.push({
372
+ dispose: () =>
373
+ devtoolsEvents.removeListener("fromDevTools", devToolsEventListener),
374
+ });
375
+
376
+ context.subscriptions.push(
377
+ commands.registerCommand("apollographql/startDevToolsServer", () => {
378
+ const port = workspace
379
+ .getConfiguration("apollographql")
380
+ .get("devTools.serverPort", 0);
381
+ startServer(port);
382
+ }),
383
+ );
384
+ context.subscriptions.push({
385
+ dispose() {
386
+ if (serverState) {
387
+ serverState.disposable.dispose();
388
+ }
389
+ },
390
+ });
391
+ context.subscriptions.push(
392
+ commands.registerCommand("apollographql/stopDevToolsServer", () => {
393
+ serverState?.disposable.dispose();
394
+ }),
395
+ );
396
+
397
+ Debug.traceLevel = workspace
398
+ .getConfiguration("apollographql")
399
+ .get("trace.server");
400
+ context.subscriptions.push(
401
+ workspace.onDidChangeConfiguration((event) => {
402
+ const affected = event.affectsConfiguration("apollographql.trace.server");
403
+ if (affected) {
404
+ Debug.traceLevel = workspace
405
+ .getConfiguration("apollographql")
406
+ .get("trace.server");
407
+ }
408
+ }),
409
+ );
410
+
332
411
  await client.start();
333
412
  return {
334
413
  outputChannel,
@@ -41,6 +41,11 @@ if (test === origTest.skip) {
41
41
  );
42
42
  }
43
43
 
44
+ if (process.platform === "win32") {
45
+ console.info("Skipping rover E2E tests in Windows");
46
+ test = origTest.skip;
47
+ }
48
+
44
49
  let editor: TextEditor;
45
50
  let getPosition: GetPositionFn;
46
51
  beforeAll(async () => {
@@ -63,13 +63,13 @@ test("wrong token", async () => {
63
63
 
64
64
  // currently, this logs twice, along with a full stracktrace, but no indication of where it came from
65
65
  // this should be improved on.
66
- expect(output).toContain(
66
+ expect(output.replace(/\s/g, "")).toContain(
67
67
  `
68
68
  [GraphQL error]: HTTP fetch failed from 'kotlin': 406: Not Acceptable
69
69
  [GraphQL error]: Invalid credentials provided
70
70
  ApolloError: HTTP fetch failed from 'kotlin': 406: Not Acceptable
71
71
  Invalid credentials provided
72
- at new ApolloError`.trim(),
72
+ at new ApolloError`.replace(/\s/g, ""),
73
73
  );
74
74
  } finally {
75
75
  await mocks.loadDefaultMocks(baseUri);
@@ -2,7 +2,7 @@ import { minimatch } from "minimatch";
2
2
  import { globSync } from "glob";
3
3
  import { invariant } from "../tools";
4
4
  import { URI } from "vscode-uri";
5
- import { normalizeURI } from "./utilities";
5
+ import { normalizeURI, withUnixSeparator } from "./utilities";
6
6
  import { resolve } from "path";
7
7
 
8
8
  export class FileSet {
@@ -35,13 +35,13 @@ export class FileSet {
35
35
  this.includes.some((include) => {
36
36
  return minimatch(
37
37
  normalizedFilePath,
38
- resolve(this.rootURI.fsPath, include),
38
+ withUnixSeparator(resolve(this.rootURI.fsPath, include)),
39
39
  );
40
40
  }) &&
41
41
  !this.excludes.some((exclude) => {
42
42
  return minimatch(
43
43
  normalizedFilePath,
44
- resolve(this.rootURI.fsPath, exclude),
44
+ withUnixSeparator(resolve(this.rootURI.fsPath, exclude)),
45
45
  );
46
46
  })
47
47
  );
@@ -48,6 +48,7 @@ const fileAssociations: { [extension: string]: string } = {
48
48
  ".re": "reason",
49
49
  ".ex": "elixir",
50
50
  ".exs": "elixir",
51
+ ".ipynb": "python",
51
52
  };
52
53
 
53
54
  export interface GraphQLInternalProjectConfig extends GraphQLProjectConfig {
@@ -157,13 +157,14 @@ connection.onInitialized(async () => {
157
157
  });
158
158
 
159
159
  const documents = new TextDocuments(TextDocument);
160
+ const schemes = ["file", "vscode-notebook-cell"];
160
161
 
161
162
  // Make the text document manager listen on the connection
162
163
  // for open, change and close text document events
163
164
  documents.listen(connection);
164
165
 
165
- function isFile(uri: string) {
166
- return URI.parse(uri).scheme === "file";
166
+ function isEnabledDocument(uri: string) {
167
+ return schemes.includes(URI.parse(uri).scheme);
167
168
  }
168
169
 
169
170
  documents.onDidChangeContent((params) => {
@@ -173,8 +174,7 @@ documents.onDidChangeContent((params) => {
173
174
  );
174
175
  if (!project) return;
175
176
 
176
- // Only watch changes to files
177
- if (!isFile(params.document.uri)) {
177
+ if (!isEnabledDocument(params.document.uri)) {
178
178
  return;
179
179
  }
180
180
 
@@ -213,8 +213,7 @@ connection.onDidChangeWatchedFiles((params) => {
213
213
  continue;
214
214
  }
215
215
 
216
- // Only watch changes to files
217
- if (!isFile(uri)) {
216
+ if (!isEnabledDocument(uri)) {
218
217
  continue;
219
218
  }
220
219
 
@@ -1,6 +1,6 @@
1
1
  import { URI } from "vscode-uri";
2
2
 
3
- const withUnixSeparator = (uriString: string) =>
3
+ export const withUnixSeparator = (uriString: string) =>
4
4
  uriString.split(/[\/\\]/).join("/");
5
5
 
6
6
  export const normalizeURI = (uriString: string) => {
@@ -30,7 +30,7 @@ export const minimumKnownExtensions: Record<
30
30
  typescriptreact: [".tsx"],
31
31
  vue: [".vue"],
32
32
  svelte: [".svelte"],
33
- python: [".py"],
33
+ python: [".py", ".ipynb"],
34
34
  ruby: [".rb"],
35
35
  dart: [".dart"],
36
36
  reason: [".re"],
package/start-ac.mjs ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ // for testing, start a few of these, e.g. with
4
+ // while true; do echo "foo\nbar\nbaz" | parallel ./start-ac.mjs; sleep 1; done
5
+
6
+ import { connectApolloClientToVSCodeDevTools } from "@apollo/client-devtools-vscode";
7
+ import WebSocket from "ws";
8
+ import { ApolloClient, InMemoryCache } from "@apollo/client/core/index.js";
9
+ import { MockLink } from "@apollo/client/testing/core/index.js";
10
+ import gql from "graphql-tag";
11
+
12
+ globalThis.WebSocket ||= WebSocket;
13
+
14
+ const helloWorld = gql`
15
+ query {
16
+ hello
17
+ }
18
+ `;
19
+
20
+ const link = new MockLink([
21
+ {
22
+ request: { query: helloWorld },
23
+ result: { data: { hello: "world" } },
24
+ maxUsageCount: 1000,
25
+ },
26
+ {
27
+ request: {
28
+ query: gql`
29
+ query {
30
+ hi
31
+ }
32
+ `,
33
+ },
34
+ result: { data: { hi: "universe" } },
35
+ maxUsageCount: 1000,
36
+ },
37
+ ]);
38
+ const client = new ApolloClient({
39
+ link,
40
+ cache: new InMemoryCache(),
41
+ devtools: { name: process.argv[2] },
42
+ });
43
+ client.watchQuery({ query: helloWorld }).subscribe({ next() {} });
44
+ const { connectedPromise, disconnect, onCleanup } =
45
+ connectApolloClientToVSCodeDevTools(
46
+ client,
47
+ "ws://localhost:7095", // nosemgrep
48
+ );
49
+ console.log("connecting...");
50
+ onCleanup((reason) =>
51
+ console.log(
52
+ "disconnected",
53
+ reason,
54
+ /* referencing client here to prevent it from getting garbage connected */ client.version,
55
+ ),
56
+ );
57
+ connectedPromise.then(() => {
58
+ console.log("connected");
59
+ // setTimeout(unregister, 5000, "USERLAND_TIMEOUT");
60
+ });