vscode-apollo 2.3.2 → 2.3.4

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.
@@ -6,7 +6,7 @@ orbs:
6
6
  executors:
7
7
  node:
8
8
  docker:
9
- - image: cimg/node:22.8.0
9
+ - image: cimg/node:22.9.0
10
10
  working_directory: ~/vscode-graphql
11
11
 
12
12
  commands:
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 2.3.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#224](https://github.com/apollographql/vscode-graphql/pull/224) [`b7d300f7`](https://github.com/apollographql/vscode-graphql/commit/b7d300f7d44138ef656bf5e5359dc284d41b78e2) Thanks [@phryneas](https://github.com/phryneas)! - Fix a situation where config files using `require` would not be imported as CommonJS.
8
+
9
+ ## 2.3.3
10
+
11
+ ### Patch Changes
12
+
13
+ - [#191](https://github.com/apollographql/vscode-graphql/pull/191) [`2e56f42d`](https://github.com/apollographql/vscode-graphql/commit/2e56f42d172a7ec8afd003f056aeabac9eab1789) Thanks [@svc-secops](https://github.com/svc-secops)! - Chores: update various dependencies (#181, #191, #217, #218)
14
+
3
15
  ## 2.3.2
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  </div>
11
11
 
12
- GraphQL has the potential to create incredible developer experiences, thanks to its strongly typed schema and query language. The Apollo platform brings these possibilities to life by enhancing your editor with rich metadata from your graph API.
12
+ Thanks to its strongly typed schema and query language, GraphQL has the potential to create incredible developer experiences. The Apollo platform brings these possibilities to life by enhancing your editor with rich metadata from your graph API.
13
13
 
14
14
  ![demo](https://raw.githubusercontent.com/apollographql/vscode-graphql/main/images/marketplace/jump-to-def.gif)
15
15
 
@@ -17,8 +17,9 @@ The Apollo GraphQL extension for VS Code brings an all-in-one tooling experience
17
17
 
18
18
  - Add [syntax highlighting](#syntax-highlighting) for GraphQL files and gql templates inside JavaScript files
19
19
  - Get instant feedback and [intelligent autocomplete](#intelligent-autocomplete) for fields, arguments, types, and variables as you write queries
20
- - Manage client side schema alongside remote schema
20
+ - Manage client-side schema alongside remote schema
21
21
  - See [performance information](#performance-insights) inline with your query definitions
22
+ - Extra features to help you with [supergraph editing](#supergraph-editing)
22
23
  - Validate field and argument usage in operations
23
24
  - [Navigate projects more easily](#navigating-projects) with jump-to and peek-at definitions
24
25
  - Manage [client-only](#client-only-schemas) schemas
@@ -26,24 +27,22 @@ The Apollo GraphQL extension for VS Code brings an all-in-one tooling experience
26
27
 
27
28
  <h2 id="getting-started">Getting started</h2>
28
29
 
29
- For the VS Code plugin to know how to find the schema, it needs to be linked to either a published schema or a local one.
30
-
31
- First, create an `apollo.config.json` file at the root of the project.
30
+ The VS Code plugin must be linked to a published or local schema. To do so, create an `apollo.config.json` file at the root of the project.
32
31
  Alternatively, you can create a `yaml`, `cjs`, `mjs`, or `ts` file with the same configuration.
33
32
 
34
33
  For the contents of this configuration file, select one of these options:
35
34
 
36
- <h3>Configure extension for schemas published to Apollo GraphOS</h3>
35
+ <h3>Configure extension for client development with schemas published to Apollo GraphOS</h3>
37
36
  <details>
38
37
  <summary>
39
38
  <i>Expand for instructions.</i>
40
39
  </summary>
41
40
 
42
- To get all the benefits of the VS Code experience, it's best to link the schema that is being developed against before installing the extension. The best way to do that is by [publishing a schema](https://www.apollographql.com/docs/graphos/delivery/publishing-schemas/) to the Apollo schema registry.
41
+ To get all the benefits of the VS Code experience, it's best to link the schema being developed before installing the extension. The best way to do that is by [publishing a schema](https://www.apollographql.com/docs/graphos/delivery/publishing-schemas/) to the [GraphOS schema registry](https://www.apollographql.com/docs/graphos#core-features).
43
42
 
44
43
  After that's done, edit the `apollo.config.json` file to look like this:
45
44
 
46
- ```json
45
+ ```jsonc
47
46
  {
48
47
  "client": {
49
48
  "service": "graphos-graph-name"
@@ -55,31 +54,68 @@ The `service` name is the name of the graph you've created in [GraphOS Studio](h
55
54
 
56
55
  See [additional configuration options](#additional-apollo-config-options).
57
56
 
58
- To authenticate with GraphOS Studio to pull down your schema, create a `.env` file in the same directory as the `apollo.config.json` file. This should be an untracked file (that is, don't commit it to Git).
57
+ To authenticate with GraphOS Studio to pull down your schema, create an `.env` file in the same directory as the `apollo.config.json` file. The `.env` file should be untrackedthat is, don't commit it to Git.
59
58
 
60
- Then go to your [User Settings page](https://studio.apollographql.com/user-settings/api-keys?referrer=docs-content) in GraphOS Studio to create a new Personal API key.
59
+ Then, go to your [User Settings page](https://studio.apollographql.com/user-settings/api-keys?referrer=docs-content) in GraphOS Studio to create a new personal API key.
61
60
 
62
- > It is best practice to create a new API key for each member of the team and name the key so its easy to find and revoke if needed. This will be easier to manage in the future.
61
+ > It's best practice to create a new API key for each team member. API keys should also be named so they're easy to find and revoke if needed.
63
62
 
64
- After the key is found, add the following line to the `.env` file:
63
+ After you've created your API key, add the following line to the `.env` file:
65
64
 
66
65
  ```bash showLineNumbers=false
67
66
  APOLLO_KEY=<enter copied key here>
68
67
  ```
69
68
 
70
- After this is done, VS Code can be reloaded and the Apollo integration will connect to GraphOS Studio to provide autocomplete, validation, and more.
69
+ Afterward, reload VS Code. The Apollo integration will connect to GraphOS Studio to provide autocomplete, validation, and more.
71
70
 
72
71
  </details>
73
72
 
74
- <h3 id="local-schemas">Configure extension to use introspection from a locally running service</h3>
73
+ <h3>Configure extension for supergraph schema development</h3>
75
74
  <details>
76
75
  <summary>
77
76
  <i>Expand for instructions.</i>
78
77
  </summary>
79
78
 
80
- Sometimes it may make sense to link the editor to a locally running version of a schema to try out new designs that are in active development. To do this, the `apollo.config.json` file can be linked to a local service definition:
79
+ The extension can integrate with the [Rover CLI](https://www.apollographql.com/docs/rover/) to help you design supergraph schemas with additional support for Apollo Federation.
80
+
81
+ Ensure you've [installed](https://www.apollographql.com/docs/rover/getting-started) and [configured](https://www.apollographql.com/docs/rover/configuring) the [latest Rover release](https://github.com/apollographql/rover/releases).
82
+
83
+ Next edit your `apollo.config.json` to look like this:
84
+
85
+ ```jsonc
86
+ {
87
+ "rover": {
88
+ // optional, if your rover binary is in PATH it will automatically be detected
89
+ "bin": "/path/to/rover",
90
+ // optional, defaults to `supergraph.yaml` in the folder of the configuration file
91
+ "supergraphConfig": "/path/to/supergraph.yaml",
92
+ // optional, defaults to the Rover default profile
93
+ "profile": ""
94
+ }
95
+ }
96
+ ```
97
+
98
+ Since all these options are optional, you can specify only the `rover` key to indicate you're using Rover for schema development rather than client development:
81
99
 
82
- ```json
100
+ ```jsonc
101
+ {
102
+ "rover": {}
103
+ }
104
+ ```
105
+
106
+ Afterward, reload VS Code. The Apollo extension will start using Rover to help you build your supergraph.
107
+
108
+ </details>
109
+
110
+ <h3 id="local-schemas">Configure extension for client development with introspection from a locally running service</h3>
111
+ <details>
112
+ <summary>
113
+ <i>Expand for instructions.</i>
114
+ </summary>
115
+
116
+ To experiment with designs under active development, you can link the editor to a locally running version of a schema. Link the `apollo.config.json` file to a local service definition like so:
117
+
118
+ ```jsonc
83
119
  {
84
120
  "client": {
85
121
  "service": {
@@ -90,11 +126,11 @@ Sometimes it may make sense to link the editor to a locally running version of a
90
126
  }
91
127
  ```
92
128
 
93
- Linking to the local schema won't provide all features, such as switching graph variants and performance metrics.
129
+ Linking to local schemas won't provide all extension features, such as switching graph variants and performance metrics.
94
130
 
95
131
  </details>
96
132
 
97
- <h3 id="local-schema-files">Configure extension for local schema files</h3>
133
+ <h3 id="local-schema-files">Configure extension for client development with local schema files</h3>
98
134
  <details>
99
135
  <summary>
100
136
  <i>Expand for instructions.</i>
@@ -104,7 +140,7 @@ You might not always have a running server to link to, so the extension also sup
104
140
  This is useful for working on a schema in isolation or for testing out new features.
105
141
  To link to a local schema file, add the following to the `apollo.config.json` file:
106
142
 
107
- ```json
143
+ ```jsonc
108
144
  {
109
145
  "client": {
110
146
  "service": {
@@ -117,13 +153,17 @@ To link to a local schema file, add the following to the `apollo.config.json` fi
117
153
 
118
154
  </details>
119
155
 
120
- <h3 id="client-only-schemas">Adding Client-only schemas</h3>
156
+ <h3 id="client-only-schemas">Bonus: Adding client-only schemas</h3>
157
+ <details>
158
+ <summary>
159
+ <i>Expand for instructions.</i>
160
+ </summary>
121
161
 
122
- One of the best features of the VS Code extension is the automatic merging of remote schemas and local ones when using integrated state management with Apollo Client. This happens automatically whenever schema definitions are found within a client project. By default, the VS Code extension will look for all JavaScript, TypeScript and GraphQL files under `./src` to find both the operations and schema definitions for building a complete schema for the application.
162
+ One of the best features of the VS Code extension is the automatic merging of remote and local schemas when using integrated state management with Apollo Client. This happens automatically whenever schema definitions are found within a client project. By default, the VS Code extension will look for all JavaScript, TypeScript, and GraphQL files under `./src` to find both the operations and schema definitions for building a complete schema for the application.
123
163
 
124
- Client side schema definitions can be spread throughout the client app project and will be merged together to create one single schema. The default behavior can be controlled by adding specifications to the `apollo.config.json`:
164
+ Client-side schema definitions can be spread throughout the client app project and will be merged to create one single schema. You can set the default behavior by adding specifications to the `apollo.config.json`:
125
165
 
126
- ```json
166
+ ```jsonc
127
167
  {
128
168
  "client": {
129
169
  // "service": <your service configuration>,
@@ -135,17 +175,19 @@ Client side schema definitions can be spread throughout the client app project a
135
175
  }
136
176
  ```
137
177
 
178
+ </details>
179
+
138
180
  <h3 id="get-the-extension">Get the extension</h3>
139
181
 
140
182
  Once you have a config set up and a schema published, [install the Apollo GraphQL extension](https://marketplace.visualstudio.com/items?itemName=apollographql.vscode-apollo), then try opening a file containing a GraphQL operation.
141
183
 
142
- When a file open, clicking the status bar icon will open the output window and print stats about the project associated with that file. This is helpful when confirming the project is setup properly.
184
+ After opening a file, click the status bar icon to open the output window and see stats about the project associated with that file. This is helpful for confirming that the project is set up properly.
143
185
 
144
186
  <img src="https://raw.githubusercontent.com/apollographql/vscode-graphql/main/images/marketplace/stats.gif" alt="Clicking the status bar icon to open the output pane">
145
187
 
146
188
  <h2 id="features">Features</h2>
147
189
 
148
- Apollo for VS Code brings many helpful features for working on a GraphQL project.
190
+ Apollo for VS Code offers a range of useful features for working on GraphQL projects.
149
191
 
150
192
  <h3 id="intelligent-autocomplete">Intelligent autocomplete</h3>
151
193
 
@@ -155,24 +197,24 @@ Once configured, VS Code has full knowledge of the schema clients are running op
155
197
 
156
198
  <h3 id="errors-and-warnings">Inline errors and warnings</h3>
157
199
 
158
- VS Code can use local or published schemas to validate operations before running them. **Syntax errors**, **invalid fields or arguments**, and even **deprecated fields** instantly appear as errors or warnings right in your editor, ensuring all developers are working with the most up-to-date production schemas.
200
+ VS Code can use local or published schemas to validate operations before running them. **Syntax errors**, **invalid fields or arguments**, and even **deprecated fields** instantly appear as errors or warnings in your editor, ensuring your entire team is working with the most up-to-date production schemas.
159
201
 
160
202
  <img src="https://raw.githubusercontent.com/apollographql/vscode-graphql/main/images/marketplace/warnings-and-errors.gif" alt="tooltip showing a field deprecation warning and error">
161
203
 
162
204
  <h3 id="field-type-info">Inline field type information</h3>
163
205
 
164
- Because of GraphQL's strongly-typed schema, VS Code not only know about which fields and arguments are valid, but also what types are expected. Hover over any type in a valid GraphQL operation to see what type that field returns and whether or not it can be null.
206
+ Because of GraphQL's strongly typed schema, VS Code knows not only which fields and arguments are valid, but also what types are expected. Hover over any type in a valid GraphQL operation to see what type that field returns and whether or not it can be null.
165
207
 
166
208
  <img src="https://raw.githubusercontent.com/apollographql/vscode-graphql/main/images/marketplace/type-info.png" style="max-width:800px;" alt="a tooltip showing a Boolean type for a field">
167
209
 
168
210
  <h3 id="performance-insights">Performance insights</h3>
169
211
 
170
- GraphQL's flexibility can make it difficult to predict the cost of an operation. Without insight into how expensive an operation is, developers can accidentally write queries that place strain on their graph API's underlying backends. Thanks to the Apollo platform's integration with VS Code and our trace warehouse, teams can avoid these performance issues altogether by instantly seeing the cost of a query right in their editor.
212
+ GraphQL's flexibility can make it difficult to predict the cost of an operation. Without insight into how expensive an operation is, developers can accidentally write queries that place strain on their graph API's underlying backends. Thanks to the Apollo platform's integration with VS Code and our trace warehouse, teams can avoid these performance issues by instantly seeing the cost of a query in their editor.
171
213
 
172
- The VS Code extension will show inline performance diagnostics when connected to a service with reported metrics in GraphOS Studio. As operations are typed, any fields that take longer than 1 ms to respond will be annotated to the right of the field inline! This gives team members a picture of how long the operation will take as more and more fields are added to operations or fragments.
214
+ The VS Code extension will show inline performance diagnostics when connected to a service with reported metrics in GraphOS Studio. As operations are typed, any fields that take longer than 1 ms to respond will be annotated to the right of the field inline. This shows team members how long the operation will take as more and more fields are added to operations or fragments.
173
215
 
174
216
  <img
175
- src="https://raw.githubusercontent.com/apollographql/vscode-graphql/80a6ca4ae59173b8cef25020345e4ebe202eec41/images/marketplace/perf-annotation.png"
217
+ src="https://raw.githubusercontent.com/apollographql/vscode-graphql/main/images/marketplace/perf-annotation.png"
176
218
  width="80%"
177
219
  style="margin: 5%"
178
220
  alt="Performance annotation next to a field"
@@ -180,11 +222,20 @@ The VS Code extension will show inline performance diagnostics when connected to
180
222
 
181
223
  <h3 id="syntax-highlighting">Syntax highlighting</h3>
182
224
 
183
- Apollo's editor extension provides syntax highlighting for all things GraphQL, including schema definitions in `.graphql` files, complex queries in TypeScript, and even client-only schema extensions. Syntax highlighting for GraphQL works out-of-the-box for `.graphql`, `.gql`, `.js` and `.ts` file types.
225
+ Apollo's editor extension provides syntax highlighting for all things GraphQL, including schema definitions in `.graphql` files, complex queries in TypeScript, and even client-only schema extensions. Syntax highlighting for GraphQL works out-of-the-box in GraphQL, JavaScript, TypeScript, Python, Lua, Ruby, Dart, Elixir, and ReasonML files.
226
+
227
+ <h3 id="supergraph-editing">supergraph editing</h3>
228
+
229
+ The extension provides features for supergraph editing, such as support for Federation directives, subgraph-spanning go-to-definition, and reporting composition errors directly to the **Problems** panel.
230
+
231
+ <img
232
+ src="https://raw.githubusercontent.com/apollographql/vscode-graphql/main/images/marketplace/federation-directive-hover.png"
233
+ alt="Hover on Federation directive"
234
+ />
184
235
 
185
236
  <h3 id="navigating-projects">Navigating projects</h3>
186
237
 
187
- Navigating large codebases can be difficult, but the Apollo GraphQL extension makes this easier. Right-clicking on any field in operations or schemas gives you the ability to jump to (or peek at) definitions, as well as find any other references to that field in your project.
238
+ Navigating large codebases can be difficult, but the Apollo GraphQL extension makes this easier. Right-clicking on any field in operations or schemas allows you to jump to (or peek at) definitions and find any other references to that field in your project.
188
239
 
189
240
  <img src="https://raw.githubusercontent.com/apollographql/vscode-graphql/main/images/marketplace/jump-to-def.gif" alt="Using jump to definition on a fragment">
190
241
 
@@ -194,11 +245,11 @@ Apollo supports publishing multiple versions ([variants](https://www.apollograph
194
245
 
195
246
  <h2 id="troubleshooting">Troubleshooting</h2>
196
247
 
197
- The most common errors are configuration errors, like a missing `.env` file or incorrect service information in the `apollo.config.js` file. Please see [the Apollo config docs](https://www.apollographql.com/docs/devtools/apollo-config/) for more configuration guidance.
248
+ The most common errors are configuration errors, like a missing `.env` file or incorrect service information in the `apollo.config.json` file. Please see [the Apollo config docs](https://www.apollographql.com/docs/devtools/apollo-config/) for more configuration guidance.
198
249
 
199
- Other errors may be caused from an old version of a published schema. To reload a schema, open the Command Palette (`cmd + shift + p` on mac), search "Apollo" and choose the "Apollo: Reload Schema" option.
250
+ An old version of a published schema may cause other errors. To reload a schema, open the **Command Palette** (`cmd + shift + p` on Mac), search for "Apollo." Choose the **Apollo: Reload Schema** option.
200
251
 
201
- Sometimes errors will show up as a notification at the bottom of your editor. Other, less critical, messages may be shown in the output pane of the editor. To open the output pane and get diagnostic information about the extension and the current service loaded (if working with a client project), just click the "Apollo GraphQL" icon in the status bar at the bottom.
252
+ Sometimes, errors will appear as a notification at the bottom of your editor. Other, less critical, messages may be shown in the output pane of the editor. To open the output pane and get diagnostic information about the extension and the current service loaded (if working with a client project), click the Apollo GraphQL icon in the status bar at the bottom.
202
253
 
203
254
  <img src="https://raw.githubusercontent.com/apollographql/vscode-graphql/main/images/marketplace/stats.gif" alt="Clicking the status bar icon to open the output pane">
204
255
 
@@ -214,7 +265,7 @@ _Optional_ - custom tagged template literal.
214
265
 
215
266
  When using GraphQL with JavaScript or TypeScript projects, it is common to use the `gql` tagged template literal to write out operations. Apollo tools look through your files for the `gql` tag to extract your queries, so if you use a different template literal, you can configure it like so:
216
267
 
217
- ```json
268
+ ```jsonc
218
269
  {
219
270
  "client": {
220
271
  "tagName": "graphql",
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.2",
5
+ "version": "2.3.4",
6
6
  "referenceID": "87197759-7617-40d0-b32e-46d378e907c7",
7
7
  "author": "Apollo GraphQL <opensource@apollographql.com>",
8
8
  "license": "MIT",
@@ -37,9 +37,9 @@
37
37
  "vscode": "^1.90.0"
38
38
  },
39
39
  "dependencies": {
40
- "@apollo/client": "3.11.4",
41
- "@apollo/subgraph": "2.8.4",
42
- "@graphql-tools/schema": "10.0.5",
40
+ "@apollo/client": "3.11.8",
41
+ "@apollo/subgraph": "2.9.1",
42
+ "@graphql-tools/schema": "10.0.6",
43
43
  "@wry/equality": "0.5.7",
44
44
  "cosmiconfig": "9.0.0",
45
45
  "dotenv": "16.4.5",
@@ -55,6 +55,7 @@
55
55
  "lz-string": "1.5.0",
56
56
  "minimatch": "10.0.1",
57
57
  "moment": "2.30.1",
58
+ "semver": "7.6.3",
58
59
  "undici": "6.19.8",
59
60
  "vscode-languageclient": "9.0.1",
60
61
  "vscode-languageserver": "9.0.1",
@@ -62,7 +63,7 @@
62
63
  "vscode-uri": "3.0.8",
63
64
  "which": "4.0.0",
64
65
  "zod": "3.23.8",
65
- "zod-validation-error": "3.3.1"
66
+ "zod-validation-error": "3.4.0"
66
67
  },
67
68
  "devDependencies": {
68
69
  "@apollo/rover": "0.27.0-alpha.0",
@@ -70,7 +71,7 @@
70
71
  "@changesets/cli": "2.26.2",
71
72
  "@graphql-codegen/cli": "^5.0.2",
72
73
  "@graphql-codegen/typescript-operations": "^4.2.3",
73
- "@types/jest": "29.5.12",
74
+ "@types/jest": "29.5.13",
74
75
  "@types/lodash.debounce": "4.0.9",
75
76
  "@types/lodash.merge": "4.6.9",
76
77
  "@types/lodash.throttle": "^4.1.9",
@@ -89,7 +90,7 @@
89
90
  "import-fresh": "^3.3.0",
90
91
  "jest": "29.7.0",
91
92
  "jest-environment-node": "29.7.0",
92
- "memfs": "4.11.1",
93
+ "memfs": "4.11.2",
93
94
  "npm-run-all": "^4.1.5",
94
95
  "prettier": "3.0.3",
95
96
  "rimraf": "6.0.1",
@@ -4,14 +4,31 @@
4
4
  "$ref": "#/definitions/baseConfig"
5
5
  },
6
6
  {
7
- "type": "object",
8
- "properties": {
9
- "client": {
10
- "$ref": "#/definitions/clientConfig"
7
+ "anyOf": [
8
+ {
9
+ "type": "object",
10
+ "properties": {
11
+ "client": {
12
+ "$ref": "#/definitions/clientConfig"
13
+ }
14
+ },
15
+ "required": [
16
+ "client"
17
+ ],
18
+ "additionalProperties": false
19
+ },
20
+ {
21
+ "type": "object",
22
+ "properties": {
23
+ "rover": {
24
+ "$ref": "#/definitions/roverConfig"
25
+ }
26
+ },
27
+ "required": [
28
+ "rover"
29
+ ],
30
+ "additionalProperties": false
11
31
  }
12
- },
13
- "required": [
14
- "client"
15
32
  ]
16
33
  }
17
34
  ],
@@ -146,6 +163,36 @@
146
163
  "additionalProperties": false,
147
164
  "description": "Configuration for a Client project."
148
165
  },
166
+ "roverConfig": {
167
+ "type": "object",
168
+ "properties": {
169
+ "bin": {
170
+ "type": "string",
171
+ "description": "The path to your Rover binary. If omitted, will look in PATH."
172
+ },
173
+ "profile": {
174
+ "type": "string",
175
+ "description": "The name of the profile to use."
176
+ },
177
+ "supergraphConfig": {
178
+ "type": [
179
+ "string",
180
+ "null"
181
+ ],
182
+ "description": "The path to your `supergraph.yaml` file. \nDefaults to a `supergraph.yaml` in the folder of your `apollo.config.json`, if there is one."
183
+ },
184
+ "extraArgs": {
185
+ "type": "array",
186
+ "items": {
187
+ "type": "string"
188
+ },
189
+ "default": [],
190
+ "description": "Extra arguments to pass to the Rover CLI."
191
+ }
192
+ },
193
+ "additionalProperties": false,
194
+ "description": "Configuration for a federated project."
195
+ },
149
196
  "engineConfig": {
150
197
  "type": "object",
151
198
  "properties": {
@@ -173,6 +220,9 @@
173
220
  "client": {
174
221
  "description": "Configuration for a Client project."
175
222
  },
223
+ "rover": {
224
+ "description": "Configuration for a federated project."
225
+ },
176
226
  "service": {
177
227
  "description": "This option is no longer supported, please remove it from your configuration file."
178
228
  }
@@ -18,7 +18,6 @@ async function main() {
18
18
  const TEST_PORT = 7096;
19
19
  process.env.APOLLO_ENGINE_ENDPOINT = "http://localhost:7096/apollo";
20
20
  process.env.MOCK_SERVER_PORT = String(TEST_PORT);
21
- process.env.APOLLO_FEATURE_FLAGS = "rover";
22
21
  disposables.push(
23
22
  ...(await Promise.all([
24
23
  runMockServer(TEST_PORT, false, loadDefaultMocks),
package/src/build.js CHANGED
@@ -68,7 +68,7 @@ const buildJsonSchemaPlugin = {
68
68
  const {
69
69
  configSchema,
70
70
  clientConfig,
71
- // roverConfig,
71
+ roverConfig,
72
72
  engineConfig,
73
73
  baseConfig,
74
74
  // @ts-ignore
@@ -78,7 +78,7 @@ const buildJsonSchemaPlugin = {
78
78
  errorMessages: true,
79
79
  definitions: {
80
80
  clientConfig,
81
- //roverConfig,
81
+ roverConfig,
82
82
  engineConfig,
83
83
  baseConfig,
84
84
  },
@@ -1,24 +1,8 @@
1
- let { loadConfig } = require("../");
2
- let { ClientConfig, RoverConfig } = require("../config");
1
+ import { loadConfig } from "../";
2
+ import { ClientConfig, RoverConfig } from "../config";
3
3
  import * as path from "path";
4
4
  import * as fs from "fs";
5
5
 
6
- async function withFeatureFlags(flags: string, fn: () => void) {
7
- const FF = process.env.APOLLO_FEATURE_FLAGS;
8
- try {
9
- process.env.APOLLO_FEATURE_FLAGS = flags;
10
- jest.resetModules();
11
- ({ loadConfig } = require("../"));
12
- ({ ClientConfig, RoverConfig } = require("../config"));
13
- return await fn();
14
- } finally {
15
- process.env.APOLLO_FEATURE_FLAGS = FF;
16
- jest.resetModules();
17
- ({ loadConfig } = require("../"));
18
- ({ ClientConfig, RoverConfig } = require("../config"));
19
- }
20
- }
21
-
22
6
  const makeNestedDir = (dir: string) => {
23
7
  if (fs.existsSync(dir)) return;
24
8
 
@@ -109,25 +93,24 @@ Object {
109
93
  `);
110
94
  });
111
95
 
112
- it("loads with rover defaults from different dir", () =>
113
- withFeatureFlags("rover", async () => {
114
- writeFilesToDir(dir, {
115
- "apollo.config.js": `
96
+ it("loads with rover defaults from different dir", async () => {
97
+ writeFilesToDir(dir, {
98
+ "apollo.config.js": `
116
99
  module.exports = {
117
100
  rover: {
118
101
  }
119
102
  }
120
103
  `,
104
+ });
105
+ fs.mkdirSync(`${dir}/bin`);
106
+ fs.writeFileSync(`${dir}/bin/rover`, "", { mode: 0o755 });
107
+ let oldPath = process.env.PATH;
108
+ process.env.PATH = `${dir}/bin:${oldPath}`;
109
+ try {
110
+ const config = await loadConfig({
111
+ configPath: dirPath,
121
112
  });
122
- fs.mkdirSync(`${dir}/bin`);
123
- fs.writeFileSync(`${dir}/bin/rover`, "", { mode: 0o755 });
124
- let oldPath = process.env.PATH;
125
- process.env.PATH = `${dir}/bin:${oldPath}`;
126
- try {
127
- const config = await loadConfig({
128
- configPath: dirPath,
129
- });
130
- expect(config?.rawConfig).toMatchInlineSnapshot(`
113
+ expect(config?.rawConfig).toMatchInlineSnapshot(`
131
114
  Object {
132
115
  "engine": Object {
133
116
  "endpoint": "https://graphql.api.apollographql.com/api/graphql",
@@ -138,10 +121,10 @@ Object {
138
121
  },
139
122
  }
140
123
  `);
141
- } finally {
142
- process.env.PATH = oldPath;
143
- }
144
- }));
124
+ } finally {
125
+ process.env.PATH = oldPath;
126
+ }
127
+ });
145
128
 
146
129
  it("[deprecated] loads config from package.json", async () => {
147
130
  writeFilesToDir(dir, {
@@ -280,7 +263,6 @@ client:
280
263
 
281
264
  await loadConfig({
282
265
  configPath: dirPath,
283
- requireConfig: true, // this is what we're testing
284
266
  });
285
267
 
286
268
  expect(spy).toHaveBeenCalledWith(
@@ -299,7 +281,7 @@ client:
299
281
  }).catch((e: any) => e);
300
282
 
301
283
  expect(error.message).toMatch(
302
- /Config needs to contain a 'client' field./i,
284
+ /Config needs to contain either 'client' or 'rover' fields/i,
303
285
  );
304
286
  });
305
287
  });
@@ -374,18 +356,17 @@ client:
374
356
  expect(config).toBeInstanceOf(ClientConfig);
375
357
  });
376
358
 
377
- it("infers rover projects from config", () =>
378
- withFeatureFlags("rover", async () => {
379
- writeFilesToDir(dir, {
380
- "apollo.config.js": `module.exports = { rover: { bin: "/usr/bin/env" } }`,
381
- });
359
+ it("infers rover projects from config", async () => {
360
+ writeFilesToDir(dir, {
361
+ "apollo.config.js": `module.exports = { rover: { bin: "/usr/bin/env" } }`,
362
+ });
382
363
 
383
- const config = await loadConfig({
384
- configPath: dirPath,
385
- });
364
+ const config = await loadConfig({
365
+ configPath: dirPath,
366
+ });
386
367
 
387
- expect(config).toBeInstanceOf(RoverConfig);
388
- }));
368
+ expect(config).toBeInstanceOf(RoverConfig);
369
+ });
389
370
  });
390
371
 
391
372
  describe("service name", () => {
@@ -9,10 +9,9 @@ import which from "which";
9
9
  import { accessSync, constants as fsConstants, statSync } from "node:fs";
10
10
  import { AsyncLocalStorage } from "async_hooks";
11
11
  import { existsSync } from "fs";
12
-
13
- const ROVER_AVAILABLE = (process.env.APOLLO_FEATURE_FLAGS || "")
14
- .split(",")
15
- .includes("rover");
12
+ import { spawn } from "node:child_process";
13
+ import { text } from "node:stream/consumers";
14
+ import semver from "semver";
16
15
 
17
16
  function ignoredFieldWarning(
18
17
  getMessage = (path: string) =>
@@ -143,49 +142,53 @@ export const clientConfig = z
143
142
  .describe("Configuration for a Client project.");
144
143
  export type ClientConfigFormat = z.infer<typeof clientConfig>;
145
144
 
146
- export const roverConfig = z.object({
147
- bin: z
148
- .preprocess(
149
- (val) => val || which.sync("rover", { nothrow: true }) || undefined,
150
- z.string({
151
- message:
152
- "Rover binary not found. Please either install it system-wide in PATH, or provide the `bin` option. Also ensure that the binary is executable.",
153
- }),
154
- )
155
- .refine(
156
- (bin) => {
157
- try {
158
- // is executable?
159
- accessSync(bin, fsConstants.X_OK);
160
- // is a file and not a directory?
161
- return statSync(bin).isFile();
162
- } catch {
163
- return false;
164
- }
165
- },
166
- {
167
- message:
168
- "Rover binary is not marked as an executable. If you are using OS X or Linux, ensure to set the executable bit.",
169
- },
170
- )
171
- .describe("The path to your Rover binary. If omitted, will look in PATH."),
172
- profile: z.string().optional().describe("The name of the profile to use."),
173
- supergraphConfig: z
174
- .preprocess((value) => {
175
- if (value !== undefined) return value;
176
- const configPath = contextStore.getStore()?.configPath!;
177
- const supergraphConfig = join(configPath, "supergraph.yaml");
178
- return existsSync(supergraphConfig) ? supergraphConfig : undefined;
179
- }, z.string().nullable().optional())
180
- .describe(
181
- "The path to your `supergraph.yaml` file. \n" +
182
- "Defaults to a `supergraph.yaml` in the folder of your `apollo.config.js`, if there is one.",
183
- ),
184
- extraArgs: z
185
- .array(z.string())
186
- .default([])
187
- .describe("Extra arguments to pass to the Rover CLI."),
188
- });
145
+ export const roverConfig = z
146
+ .object({
147
+ bin: z
148
+ .preprocess(
149
+ (val) => val || which.sync("rover", { nothrow: true }) || undefined,
150
+ z.string({
151
+ message:
152
+ "Rover binary not found. Please either install it system-wide in PATH, or provide the `bin` option. Also ensure that the binary is executable.",
153
+ }),
154
+ )
155
+ .refine(
156
+ (bin) => {
157
+ try {
158
+ // is executable?
159
+ accessSync(bin, fsConstants.X_OK);
160
+ // is a file and not a directory?
161
+ return statSync(bin).isFile();
162
+ } catch {
163
+ return false;
164
+ }
165
+ },
166
+ {
167
+ message:
168
+ "Rover binary is not marked as an executable. If you are using OS X or Linux, ensure to set the executable bit.",
169
+ },
170
+ )
171
+ .describe(
172
+ "The path to your Rover binary. If omitted, will look in PATH.",
173
+ ),
174
+ profile: z.string().optional().describe("The name of the profile to use."),
175
+ supergraphConfig: z
176
+ .preprocess((value) => {
177
+ if (value !== undefined) return value;
178
+ const configPath = contextStore.getStore()?.configPath || ".";
179
+ const supergraphConfig = join(configPath, "supergraph.yaml");
180
+ return existsSync(supergraphConfig) ? supergraphConfig : undefined;
181
+ }, z.string().nullable().optional())
182
+ .describe(
183
+ "The path to your `supergraph.yaml` file. \n" +
184
+ "Defaults to a `supergraph.yaml` in the folder of your `apollo.config.json`, if there is one.",
185
+ ),
186
+ extraArgs: z
187
+ .array(z.string())
188
+ .default([])
189
+ .describe("Extra arguments to pass to the Rover CLI."),
190
+ })
191
+ .describe("Configuration for a federated project.");
189
192
  type RoverConfigFormat = z.infer<typeof roverConfig>;
190
193
 
191
194
  export const engineConfig = z
@@ -212,12 +215,7 @@ export type EngineConfig = z.infer<typeof engineConfig>;
212
215
  export const baseConfig = z.object({
213
216
  engine: engineConfig.default({}),
214
217
  client: z.unknown().optional().describe(clientConfig.description!),
215
- ...ifRoverAvailable(
216
- {
217
- rover: z.unknown().optional(),
218
- },
219
- {},
220
- ),
218
+ rover: z.unknown().optional().describe(roverConfig.description!),
221
219
  service: ignoredFieldWarning(
222
220
  (path) =>
223
221
  `Service-type projects are no longer supported. Please remove the "${path}" field from your configuration file.`,
@@ -234,56 +232,36 @@ export type FullRoverConfigFormat = Extract<
234
232
  { rover: {} }
235
233
  >;
236
234
 
237
- /** Helper function for TypeScript - we just want the first type to make it into the types, not the "no feature flag" fallback */
238
- function ifRoverAvailable<T>(yes: T, no: any): T {
239
- return ROVER_AVAILABLE ? yes : no;
240
- }
241
-
242
235
  export const configSchema = baseConfig
243
236
  .superRefine((val, ctx) => {
244
- if (ROVER_AVAILABLE) {
245
- if ("client" in val && "rover" in val) {
246
- ctx.addIssue({
247
- code: "custom",
248
- message: "Config cannot contain both 'client' and 'rover' fields",
249
- fatal: true,
250
- });
251
- }
252
- if (!("client" in val) && !("rover" in val)) {
253
- ctx.addIssue({
254
- code: "custom",
255
- message: "Config needs to contain either 'client' or 'rover' fields",
256
- fatal: true,
257
- });
258
- }
259
- } else {
260
- if (!("client" in val)) {
261
- ctx.addIssue({
262
- code: "custom",
263
- message: "Config needs to contain a 'client' field.",
264
- fatal: true,
265
- });
266
- }
237
+ if ("client" in val && "rover" in val) {
238
+ ctx.addIssue({
239
+ code: "custom",
240
+ message: "Config cannot contain both 'client' and 'rover' fields",
241
+ fatal: true,
242
+ });
243
+ }
244
+ if (!("client" in val) && !("rover" in val)) {
245
+ ctx.addIssue({
246
+ code: "custom",
247
+ message: "Config needs to contain either 'client' or 'rover' fields",
248
+ fatal: true,
249
+ });
267
250
  }
268
251
  })
269
252
  .and(
270
- ifRoverAvailable(
271
- z.union([
272
- z
273
- .object({
274
- client: clientConfig,
275
- })
276
- .transform((val): typeof val & { rover?: never } => val),
277
- z
278
- .object({
279
- rover: roverConfig,
280
- })
281
- .transform((val): typeof val & { client?: never } => val),
282
- ]),
283
- z.object({
284
- client: clientConfig,
285
- }),
286
- ),
253
+ z.union([
254
+ z
255
+ .object({
256
+ client: clientConfig,
257
+ })
258
+ .transform((val): typeof val & { rover?: never } => val),
259
+ z
260
+ .object({
261
+ rover: roverConfig,
262
+ })
263
+ .transform((val): typeof val & { client?: never } => val),
264
+ ]),
287
265
  );
288
266
  export type RawApolloConfigFormat = z.input<typeof configSchema>;
289
267
  export type ParsedApolloConfigFormat = z.output<typeof configSchema>;
@@ -371,6 +349,11 @@ export abstract class ApolloConfig {
371
349
  if (this._graphId) return this._graphId;
372
350
  return getGraphIdFromConfig(this.rawConfig);
373
351
  }
352
+
353
+ /**
354
+ * execute some additional asynchronous verification steps that cannot be part of the sync parsing part
355
+ */
356
+ async verify() {}
374
357
  }
375
358
 
376
359
  export class ClientConfig extends ApolloConfig {
@@ -395,4 +378,45 @@ export class RoverConfig extends ApolloConfig {
395
378
  super(rawConfig, configURI);
396
379
  this.rover = rawConfig.rover;
397
380
  }
381
+
382
+ /**
383
+ * execute some additional asynchronous verification steps that cannot be part of the sync parsing part
384
+ */
385
+ async verify() {
386
+ try {
387
+ const child = spawn(this.rover.bin, ["-V"], {
388
+ stdio: ["pipe", "pipe", "ignore"],
389
+ });
390
+ const output = await text(child.stdout);
391
+ const versionPrefix = "Rover ";
392
+ if (output.startsWith(versionPrefix)) {
393
+ const version = output.slice(versionPrefix.length).trim();
394
+ if (!semver.valid(version)) {
395
+ // not a valid version, we accept this and will try anyways
396
+ return;
397
+ }
398
+ if (semver.gte(version, "0.27.0-alpha.0")) {
399
+ // this is a supported version
400
+ return;
401
+ }
402
+ const error = new Error(
403
+ `Rover version ${version} is not supported by the extension. Please upgrade to at least 0.27.0.`,
404
+ );
405
+ // @ts-expect-error would require a target of ES2022 in tsconfig
406
+ error.cause = "ROVER_TOO_OLD";
407
+ throw error;
408
+ }
409
+ // we can't find out the version, but we'll try anyways
410
+ } catch (error) {
411
+ if (
412
+ error &&
413
+ error instanceof Error &&
414
+ // @ts-expect-error would require a target of ES2022 in tsconfig
415
+ error.cause === "ROVER_TOO_OLD"
416
+ ) {
417
+ throw error;
418
+ }
419
+ // we ignore all other errors and will handle that when we actually spawn the rover binary
420
+ }
421
+ }
398
422
  }
@@ -121,9 +121,11 @@ export async function loadConfig({
121
121
 
122
122
  let { config, filepath } = loadedConfig;
123
123
 
124
- return parseApolloConfig(config, URI.file(resolve(filepath)), {
124
+ const finalConfig = parseApolloConfig(config, URI.file(resolve(filepath)), {
125
125
  apiKey,
126
126
  serviceName: nameFromKey,
127
127
  configPath: dirname(filepath),
128
128
  });
129
+ await finalConfig.verify();
130
+ return finalConfig;
129
131
  }
@@ -93,7 +93,8 @@ export const loadJs: Loader = async function loadJs(filepath, contents) {
93
93
  if (
94
94
  error instanceof Error &&
95
95
  // [ERROR] ReferenceError: module is not defined in ES module scope
96
- error.message.includes("module is not defined")
96
+ // [ERROR] ReferenceError: require is not defined in ES module scope
97
+ error.message.includes("is not defined in ES module scope")
97
98
  ) {
98
99
  return loadCachebustedJs(filepath, contents, "commonjs");
99
100
  } else {
@@ -23,7 +23,7 @@ import {
23
23
  rangeInContainingDocument,
24
24
  } from "../../utilities/source";
25
25
  import { URI } from "vscode-uri";
26
- import { DEBUG } from "./project";
26
+ import { Debug } from "../../utilities";
27
27
 
28
28
  export interface FilePart {
29
29
  fractionalIndex: string;
@@ -285,7 +285,7 @@ export class DocumentSynchronization {
285
285
  }
286
286
 
287
287
  handlePartDiagnostics(params: PublishDiagnosticsParams) {
288
- DEBUG && console.log("Received diagnostics", params);
288
+ Debug.traceVerbose("Received diagnostics", params);
289
289
  const uriDetails = splitUri(params.uri);
290
290
  const found = this.knownFiles.get(uriDetails.uri);
291
291
  if (!found || found.source === "lsp") {
@@ -36,8 +36,8 @@ import { VSCodeConnection } from "../../server";
36
36
  import { getLanguageIdForExtension } from "../../utilities/languageIdForExtension";
37
37
  import { extname } from "node:path";
38
38
  import type { FileExtension } from "../../../tools/utilities/languageInformation";
39
-
40
- export const DEBUG = true;
39
+ import { Debug } from "../../utilities";
40
+ import { TraceLevel } from "src/language-server/utilities/debug";
41
41
 
42
42
  export function isRoverConfig(config: ApolloConfig): config is RoverConfig {
43
43
  return config instanceof RoverConfig;
@@ -115,11 +115,14 @@ export class RoverProject extends GraphQLProject {
115
115
  params?: P,
116
116
  ): Promise<void> {
117
117
  const connection = await this.getConnection();
118
- DEBUG &&
119
- console.log("sending notification %o", {
118
+ Debug.traceMessage(
119
+ "[Rover] Sending notification: " + type.method,
120
+ "[Rover] Sending notification %o",
121
+ {
120
122
  type: type.method,
121
123
  params,
122
- });
124
+ },
125
+ );
123
126
  try {
124
127
  return await connection.sendNotification(type, params);
125
128
  } catch (error) {
@@ -136,10 +139,20 @@ export class RoverProject extends GraphQLProject {
136
139
  token?: CancellationToken,
137
140
  ): Promise<R> {
138
141
  const connection = await this.getConnection();
139
- DEBUG && console.log("sending request %o", { type: type.method, params });
142
+ Debug.traceMessage(
143
+ "[Rover] Sending request: " + type.method,
144
+ "[Rover] Sending request %o",
145
+ { type: type.method, params },
146
+ );
140
147
  try {
141
148
  const result = await connection.sendRequest(type, params, token);
142
- DEBUG && console.log({ result });
149
+ Debug.traceMessage(
150
+ "[Rover] Received response: " + type.method,
151
+ "[Rover] Received response %s\nResult: %o",
152
+ type.method,
153
+
154
+ result,
155
+ );
143
156
  return result;
144
157
  } catch (error) {
145
158
  if (error instanceof Error) {
@@ -166,27 +179,35 @@ export class RoverProject extends GraphQLProject {
166
179
  if (this.config.rover.supergraphConfig) {
167
180
  args.push("--supergraph-config", this.config.rover.supergraphConfig);
168
181
  }
182
+ if (Debug.traceLevel >= TraceLevel.verbose) {
183
+ args.push("--log", "debug");
184
+ }
169
185
  args.push(...this.config.rover.extraArgs);
170
186
 
171
- DEBUG &&
172
- console.log(`starting ${this.config.rover.bin} '${args.join("' '")}'`);
187
+ Debug.traceVerbose(
188
+ `starting ${this.config.rover.bin} '${args.join("' '")}'`,
189
+ );
173
190
  const child = cp.spawn(this.config.rover.bin, args, {
174
- env: DEBUG ? { RUST_BACKTRACE: "1" } : {},
175
- stdio: ["pipe", "pipe", DEBUG ? "inherit" : "ignore"],
191
+ env: { NO_COLOR: "1" },
192
+ stdio: [
193
+ "pipe",
194
+ "pipe",
195
+ Debug.traceLevel >= TraceLevel.verbose ? "inherit" : "ignore",
196
+ ],
176
197
  });
177
198
  this.child = child;
178
199
  const reader = new StreamMessageReader(child.stdout);
179
200
  const writer = new StreamMessageWriter(child.stdin);
180
201
  const connection = createProtocolConnection(reader, writer);
181
202
  connection.onClose(() => {
182
- DEBUG && console.log("Connection closed");
203
+ Debug.traceMessage("[Rover] Connection closed");
183
204
  child.kill();
184
205
  source.cancel();
185
206
  this._connection = undefined;
186
207
  });
187
208
 
188
209
  connection.onError((err) => {
189
- console.error({ err });
210
+ Debug.error("%o", { err });
190
211
  });
191
212
 
192
213
  connection.onNotification(
@@ -195,11 +216,14 @@ export class RoverProject extends GraphQLProject {
195
216
  );
196
217
 
197
218
  connection.onUnhandledNotification((notification) => {
198
- DEBUG && console.info("unhandled notification from LSP", notification);
219
+ Debug.traceVerbose(
220
+ "[Rover] unhandled notification from LSP",
221
+ notification,
222
+ );
199
223
  });
200
224
 
201
225
  connection.listen();
202
- DEBUG && console.log("Initializing connection");
226
+ Debug.traceMessage("[Rover] Initializing connection");
203
227
 
204
228
  const source = new CancellationTokenSource();
205
229
  try {
@@ -213,7 +237,11 @@ export class RoverProject extends GraphQLProject {
213
237
  source.token,
214
238
  );
215
239
  this.roverCapabilities = status.capabilities;
216
- DEBUG && console.log("Connection initialized", status);
240
+ Debug.traceMessage(
241
+ "[Rover] Connection initialized",
242
+ "[Rover] Connection initialized %o",
243
+ status,
244
+ );
217
245
 
218
246
  await this.connectionStorage.run(
219
247
  connection,
@@ -222,7 +250,7 @@ export class RoverProject extends GraphQLProject {
222
250
 
223
251
  return connection;
224
252
  } catch (error) {
225
- console.error("Connection failed to initialize", error);
253
+ Debug.error("Connection with Rover failed to initialize", error);
226
254
  throw error;
227
255
  }
228
256
  }
@@ -295,7 +323,7 @@ export class RoverProject extends GraphQLProject {
295
323
  if (isRequestType(SemanticTokensRequest.type, type, params)) {
296
324
  return this.documents.getFullSemanticTokens(params, token);
297
325
  } else {
298
- DEBUG && console.info("unhandled request from VSCode", { type, params });
326
+ Debug.traceVerbose("unhandled request from VSCode", { type, params });
299
327
  return undefined;
300
328
  }
301
329
  };
@@ -304,8 +332,7 @@ export class RoverProject extends GraphQLProject {
304
332
  type,
305
333
  params,
306
334
  ) => {
307
- DEBUG &&
308
- console.info("unhandled notification from VSCode", { type, params });
335
+ Debug.traceVerbose("unhandled notification from VSCode", { type, params });
309
336
  };
310
337
 
311
338
  async onVSCodeConnectionInitialized(connection: VSCodeConnection) {
@@ -7,6 +7,7 @@ import {
7
7
  TextDocumentSyncKind,
8
8
  SymbolInformation,
9
9
  FileEvent,
10
+ SetTraceNotification,
10
11
  } from "vscode-languageserver/node";
11
12
  import { TextDocument } from "vscode-languageserver-textdocument";
12
13
  import { type QuickPickItem } from "vscode";
@@ -92,11 +93,16 @@ workspace.onConfigFilesFound(async (params) => {
92
93
  );
93
94
  });
94
95
 
96
+ connection.onNotification(SetTraceNotification.type, ({ value }) => {
97
+ Debug.traceLevel = value;
98
+ });
99
+
95
100
  connection.onInitialize(
96
- async ({ capabilities, workspaceFolders, initializationOptions }) => {
101
+ async ({ capabilities, workspaceFolders, initializationOptions, trace }) => {
97
102
  const { languageIdExtensionMap } =
98
103
  initializationOptions as InitializationOptions;
99
104
  setLanguageIdExtensionMap(languageIdExtensionMap);
105
+ Debug.traceLevel = trace;
100
106
 
101
107
  hasWorkspaceFolderCapability = !!(
102
108
  capabilities.workspace && capabilities.workspace.workspaceFolders
@@ -1,5 +1,6 @@
1
1
  import { LanguageServerNotifications as Notifications } from "../../messages";
2
- import { Connection } from "vscode-languageserver/node";
2
+ import { Connection, TraceValues } from "vscode-languageserver/node";
3
+ import { format } from "util";
3
4
 
4
5
  /**
5
6
  * for errors (and other logs in debug mode) we want to print
@@ -15,9 +16,27 @@ const createAndTrimStackTrace = () => {
15
16
  : stack;
16
17
  };
17
18
 
18
- type Logger = (message?: any) => void;
19
+ type Logger = (message?: any, minLevel?: TraceLevel) => void;
20
+ export enum TraceLevel {
21
+ "off" = 0,
22
+ "messages" = 1,
23
+ "verbose" = 2,
24
+ }
19
25
 
20
26
  export class Debug {
27
+ private static _traceLevel: TraceLevel = TraceLevel.off;
28
+ public static get traceLevel(): TraceLevel {
29
+ return Debug._traceLevel;
30
+ }
31
+ public static set traceLevel(value: TraceValues | undefined) {
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 = TraceLevel.messages;
36
+ } else {
37
+ this._traceLevel = TraceLevel[value || "off"];
38
+ }
39
+ }
21
40
  private static connection?: Connection;
22
41
  private static infoLogger: Logger = (message) =>
23
42
  console.log("[INFO] " + message);
@@ -67,17 +86,39 @@ export class Debug {
67
86
  if (error) Debug.errorLogger = error;
68
87
  }
69
88
 
70
- public static info(message: string) {
71
- Debug.infoLogger(message);
89
+ public static info(message: string, ...param: any[]) {
90
+ Debug.infoLogger(format(message, ...param));
72
91
  }
73
92
 
74
- public static error(message: string) {
93
+ public static error(message: string, ...param: any[]) {
75
94
  const stack = createAndTrimStackTrace();
76
- Debug.errorLogger(`${message}\n${stack}`);
95
+ Debug.errorLogger(`${format(message, ...param)}\n${stack}`);
96
+ }
97
+
98
+ public static warning(message: string, ...param: any[]) {
99
+ Debug.warningLogger(format(message, ...param));
100
+ }
101
+
102
+ public static traceMessage(
103
+ short: string,
104
+ verbose = short,
105
+ ...verboseParams: any[]
106
+ ) {
107
+ if (Debug.traceLevel >= TraceLevel.verbose) {
108
+ // directly logging to `console` because
109
+ // we don't want to send yet another notification that will be traced
110
+ console.info(verbose, ...verboseParams);
111
+ } else if (Debug.traceLevel >= TraceLevel.messages) {
112
+ console.info(short);
113
+ }
77
114
  }
78
115
 
79
- public static warning(message: string) {
80
- Debug.warningLogger(message);
116
+ public static traceVerbose(message: string, ...params: any[]) {
117
+ if (Debug.traceLevel >= TraceLevel.verbose) {
118
+ // directly logging to `console` because
119
+ // we don't want to send yet another notification that will be traced
120
+ console.info(message, ...params);
121
+ }
81
122
  }
82
123
 
83
124
  public static sendErrorTelemetry(message: string) {