├── .eslintrc.json
├── .gitignore
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── .vscodeignore
├── CHANGELOG.md
├── README.md
├── grammars
└── graphql.json
├── images
└── github-graphql.gif
├── language
└── language-configuration.json
├── package-lock.json
├── package.json
├── src
├── extension.ts
└── test
│ ├── runTest.ts
│ └── suite
│ ├── extension.test.ts
│ └── index.ts
├── tsconfig.json
├── vsc-extension-quickstart.md
└── webpack.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser": "@typescript-eslint/parser",
4 | "parserOptions": {
5 | "ecmaVersion": 6,
6 | "sourceType": "module"
7 | },
8 | "plugins": [
9 | "@typescript-eslint"
10 | ],
11 | "rules": {
12 | "@typescript-eslint/naming-convention": "warn",
13 | "@typescript-eslint/semi": "warn",
14 | "curly": "warn",
15 | "eqeqeq": "warn",
16 | "no-throw-literal": "warn",
17 | "semi": "off"
18 | },
19 | "ignorePatterns": [
20 | "out",
21 | "dist",
22 | "**/*.d.ts"
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | dist
3 | node_modules
4 | .vscode-test/
5 | *.vsix
6 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "dbaeumer.vscode-eslint",
6 | "amodio.tsl-problem-matcher"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that compiles the extension and then opens it inside a new window
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | {
6 | "version": "0.2.0",
7 | "configurations": [
8 | {
9 | "name": "Run Extension",
10 | "type": "extensionHost",
11 | "request": "launch",
12 | "args": [
13 | "--extensionDevelopmentPath=${workspaceFolder}"
14 | ],
15 | "outFiles": [
16 | "${workspaceFolder}/dist/**/*.js"
17 | ],
18 | "preLaunchTask": "${defaultBuildTask}"
19 | },
20 | {
21 | "name": "Extension Tests",
22 | "type": "extensionHost",
23 | "request": "launch",
24 | "args": [
25 | "--extensionDevelopmentPath=${workspaceFolder}",
26 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
27 | ],
28 | "outFiles": [
29 | "${workspaceFolder}/out/test/**/*.js"
30 | ],
31 | "preLaunchTask": "npm: test-watch"
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // Place your settings in this file to overwrite default and user settings.
2 | {
3 | "files.exclude": {
4 | "out": false, // set this to true to hide the "out" folder with the compiled JS files
5 | "dist": false // set this to true to hide the "dist" folder with the compiled JS files
6 | },
7 | "search.exclude": {
8 | "out": true, // set this to false to include "out" folder in search results
9 | "dist": true // set this to false to include "dist" folder in search results
10 | },
11 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts
12 | "typescript.tsc.autoDetect": "off"
13 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | // See https://go.microsoft.com/fwlink/?LinkId=733558
2 | // for the documentation about the tasks.json format
3 | {
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "type": "npm",
8 | "script": "watch",
9 | "problemMatcher": [
10 | "$ts-webpack-watch",
11 | "$tslint-webpack-watch"
12 | ],
13 | "isBackground": true,
14 | "presentation": {
15 | "reveal": "never"
16 | },
17 | "group": {
18 | "kind": "build",
19 | "isDefault": true
20 | }
21 | },
22 | {
23 | "type": "npm",
24 | "script": "test-watch",
25 | "problemMatcher": "$tsc-watch",
26 | "isBackground": true,
27 | "presentation": {
28 | "reveal": "never"
29 | },
30 | "group": "build"
31 | }
32 | ]
33 | }
--------------------------------------------------------------------------------
/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | .vscode-test/**
3 | out/**
4 | node_modules/**
5 | src/**
6 | .gitignore
7 | .yarnrc
8 | vsc-extension-quickstart.md
9 | **/tsconfig.json
10 | **/.eslintrc.json
11 | **/*.map
12 | **/*.ts
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## [0.2.0] - 2022-04-14
4 |
5 | - Thanks to @jason-dour for contributing configurable GitHub auth scopes via the `githubGraphql.scopes` setting 🎉
6 |
7 | ## [0.1.0] - 2022-04-03
8 |
9 | - Serializer has moved to the VS Code GraphQL Notebook extension, which remains compatible with VS Code for the Web. You can continue to open `*.github-graphql-nb` files with the generic GraphQL notebook serializer
10 | - This extension now provides a convenience layer for running GitHub API queries as an alternative to configuring an endpoint using a `graphql-config` file
11 |
12 | ## [0.0.7] - 2022-02-09
13 |
14 | - Use `rebornix.vscode-code-renderer` extension to render JSON notebook output
15 | - Fix GraphQL language configuration
16 |
17 | ## [0.0.6] - 2022-02-03
18 |
19 | - Add missing JSON indentation on web
20 |
21 | ## [0.0.5] - 2022-01-09
22 |
23 | - This extension now works in https://vscode.dev and https://github.dev
24 | - github-graphql-nb file types now open in the notebook UI by default
25 |
26 | ## [0.0.4] - 2022-01-06
27 |
28 | - Markdown cells now render correctly
29 |
30 | ## [0.0.3] - 2022-01-06
31 |
32 | - You can now use variables with your queries as follows:
33 |
34 | ```graphql
35 | query ($owner: String!, $repo: String!) {
36 | repository(owner: $owner, name: $repo) {
37 | name
38 | }
39 | }
40 |
41 | variables {
42 | "owner": "eamodio",
43 | "repo": "vscode-gitlens"
44 | }
45 | ```
46 |
47 | (Thanks [@eamodio](https://github.com/eamodio)!)
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GitHub GraphQL Notebooks
2 |
3 | Interactively run [GitHub GraphQL](https://docs.github.com/en/graphql) queries and mutations right inside [VS Code notebooks](https://code.visualstudio.com/api/extension-guides/notebook)! 📓
4 |
5 |
6 |
7 | Get started:
8 | 1. File > New File... > GitHub GraphQL Notebook
9 | 2. Sign into GitHub through the built-in GitHub Authentication extension when prompted
10 | 3. Run and save your queries and mutations
11 |
--------------------------------------------------------------------------------
/grammars/graphql.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "GraphQL",
3 | "scopeName": "source.graphql",
4 | "fileTypes": ["graphql", "graphqls", "gql", "graphcool"],
5 | "patterns": [{ "include": "#graphql" }],
6 | "repository": {
7 | "graphql": {
8 | "patterns": [
9 | { "include": "#graphql-comment" },
10 | { "include": "#graphql-description-docstring" },
11 | { "include": "#graphql-description-singleline" },
12 | { "include": "#graphql-fragment-definition" },
13 | { "include": "#graphql-directive-definition" },
14 | { "include": "#graphql-type-interface" },
15 | { "include": "#graphql-enum" },
16 | { "include": "#graphql-scalar" },
17 | { "include": "#graphql-union" },
18 | { "include": "#graphql-schema" },
19 | { "include": "#graphql-operation-def" },
20 | { "include": "#literal-quasi-embedded" }
21 | ]
22 | },
23 | "graphql-operation-def": {
24 | "patterns": [
25 | { "include": "#graphql-query-mutation" },
26 | { "include": "#graphql-name" },
27 | { "include": "#graphql-variable-definitions" },
28 | { "include": "#graphql-directive" },
29 | { "include": "#graphql-selection-set" }
30 | ]
31 | },
32 | "graphql-fragment-definition": {
33 | "name": "meta.fragment.graphql",
34 | "begin": "\\s*(?:(\\bfragment\\b)\\s*([_A-Za-z][_0-9A-Za-z]*)?\\s*(?:(\\bon\\b)\\s*([_A-Za-z][_0-9A-Za-z]*)))",
35 | "end": "(?<=})",
36 | "captures": {
37 | "1": { "name": "keyword.fragment.graphql" },
38 | "2": { "name": "entity.name.fragment.graphql" },
39 | "3": { "name": "keyword.on.graphql" },
40 | "4": { "name": "support.type.graphql" }
41 | },
42 | "patterns": [
43 | { "include": "#graphql-comment" },
44 | { "include": "#graphql-description-docstring" },
45 | { "include": "#graphql-description-singleline" },
46 | { "include": "#graphql-selection-set" },
47 | { "include": "#graphql-directive" },
48 | { "include": "#graphql-skip-newlines" },
49 | { "include": "#literal-quasi-embedded" }
50 | ]
51 | },
52 | "graphql-query-mutation": {
53 | "match": "\\s*\\b(query|mutation)\\b",
54 | "captures": {
55 | "1": { "name": "keyword.operation.graphql" }
56 | }
57 | },
58 | "graphql-type-interface": {
59 | "name": "meta.type.interface.graphql",
60 | "begin": "\\s*\\b(?:(extends?)?\\b\\s*\\b(type)|(interface)|(input))\\b\\s*([_A-Za-z][_0-9A-Za-z]*)?",
61 | "end": "(?=.)",
62 | "applyEndPatternLast": 1,
63 | "captures": {
64 | "1": { "name": "keyword.type.graphql" },
65 | "2": { "name": "keyword.type.graphql" },
66 | "3": { "name": "keyword.interface.graphql" },
67 | "4": { "name": "keyword.input.graphql" },
68 | "5": { "name": "support.type.graphql" }
69 | },
70 | "patterns": [
71 | {
72 | "begin": "\\s*\\b(implements)\\b\\s*",
73 | "end": "\\s*(?={)",
74 | "beginCaptures": {
75 | "1": { "name": "keyword.implements.graphql" }
76 | },
77 | "patterns": [
78 | {
79 | "match": "\\s*([_A-Za-z][_0-9A-Za-z]*)",
80 | "captures": {
81 | "1": { "name": "support.type.graphql" }
82 | }
83 | },
84 | { "include": "#graphql-comment" },
85 | { "include": "#graphql-description-docstring" },
86 | { "include": "#graphql-description-singleline" },
87 | { "include": "#graphql-directive" },
88 | { "include": "#graphql-ampersand" },
89 | { "include": "#graphql-comma" }
90 | ]
91 | },
92 | { "include": "#graphql-comment" },
93 | { "include": "#graphql-description-docstring" },
94 | { "include": "#graphql-description-singleline" },
95 | { "include": "#graphql-directive" },
96 | { "include": "#graphql-type-object" },
97 | { "include": "#literal-quasi-embedded" },
98 | { "include": "#graphql-ignore-spaces" }
99 | ]
100 | },
101 | "graphql-ignore-spaces": {
102 | "match": "\\s*"
103 | },
104 | "graphql-type-object": {
105 | "name": "meta.type.object.graphql",
106 | "begin": "\\s*({)",
107 | "end": "\\s*(})",
108 | "beginCaptures": {
109 | "1": { "name": "punctuation.operation.graphql" }
110 | },
111 | "endCaptures": {
112 | "1": { "name": "punctuation.operation.graphql" }
113 | },
114 | "patterns": [
115 | { "include": "#graphql-comment" },
116 | { "include": "#graphql-description-docstring" },
117 | { "include": "#graphql-description-singleline" },
118 | { "include": "#graphql-object-type" },
119 | { "include": "#graphql-type-definition" },
120 | { "include": "#literal-quasi-embedded" }
121 | ]
122 | },
123 | "graphql-type-definition": {
124 | "comment": "key (optionalArgs): Type",
125 | "begin": "\\s*([_A-Za-z][_0-9A-Za-z]*)(?=\\s*\\(|:)",
126 | "end": "(?=\\s*(([_A-Za-z][_0-9A-Za-z]*)\\s*(\\(|:)|(})))|\\s*(,)",
127 | "beginCaptures": {
128 | "1": { "name": "variable.graphql" }
129 | },
130 | "endCaptures": {
131 | "5": { "name": "punctuation.comma.graphql" }
132 | },
133 | "patterns": [
134 | { "include": "#graphql-comment" },
135 | { "include": "#graphql-description-docstring" },
136 | { "include": "#graphql-description-singleline" },
137 | { "include": "#graphql-directive" },
138 | { "include": "#graphql-variable-definitions" },
139 | { "include": "#graphql-type-object" },
140 | { "include": "#graphql-colon" },
141 | { "include": "#graphql-input-types" },
142 | { "include": "#literal-quasi-embedded" }
143 | ]
144 | },
145 | "graphql-schema": {
146 | "begin": "\\s*\\b(schema)\\b",
147 | "end": "(?<=})",
148 | "beginCaptures": {
149 | "1": { "name": "keyword.schema.graphql" }
150 | },
151 | "patterns": [
152 | {
153 | "begin": "\\s*({)",
154 | "end": "\\s*(})",
155 | "beginCaptures": {
156 | "1": { "name": "punctuation.operation.graphql" }
157 | },
158 | "endCaptures": {
159 | "1": { "name": "punctuation.operation.graphql" }
160 | },
161 | "patterns": [
162 | {
163 | "begin": "\\s*([_A-Za-z][_0-9A-Za-z]*)(?=\\s*\\(|:)",
164 | "end": "(?=\\s*(([_A-Za-z][_0-9A-Za-z]*)\\s*(\\(|:)|(})))|\\s*(,)",
165 | "beginCaptures": {
166 | "1": { "name": "variable.arguments.graphql" }
167 | },
168 | "endCaptures": {
169 | "5": { "name": "punctuation.comma.graphql" }
170 | },
171 | "patterns": [
172 | {
173 | "match": "\\s*([_A-Za-z][_0-9A-Za-z]*)",
174 | "captures": {
175 | "1": { "name": "support.type.graphql" }
176 | }
177 | },
178 | { "include": "#graphql-comment" },
179 | { "include": "#graphql-description-docstring" },
180 | { "include": "#graphql-description-singleline" },
181 | { "include": "#graphql-colon" },
182 | { "include": "#graphql-skip-newlines" }
183 | ]
184 | },
185 | { "include": "#graphql-comment" },
186 | { "include": "#graphql-description-docstring" },
187 | { "include": "#graphql-description-singleline" },
188 | { "include": "#graphql-skip-newlines" }
189 | ]
190 | },
191 | { "include": "#graphql-comment" },
192 | { "include": "#graphql-description-docstring" },
193 | { "include": "#graphql-description-singleline" },
194 | { "include": "#graphql-directive" },
195 | { "include": "#graphql-skip-newlines" }
196 | ]
197 | },
198 | "graphql-comment": {
199 | "patterns": [
200 | {
201 | "comment": "need to prefix comment space with a scope else Atom's reflow cmd doesn't work",
202 | "name": "comment.line.graphql.js",
203 | "match": "(\\s*)(#).*",
204 | "captures": {
205 | "1": {
206 | "name": "punctuation.whitespace.comment.leading.graphql"
207 | }
208 | }
209 | },
210 | {
211 | "name": "comment.line.graphql.js",
212 | "begin": "(\"\"\")",
213 | "end": "(\"\"\")",
214 | "beginCaptures": {
215 | "1": {
216 | "name": "punctuation.whitespace.comment.leading.graphql"
217 | }
218 | }
219 | },
220 | {
221 | "name": "comment.line.graphql.js",
222 | "begin": "(\")",
223 | "end": "(\")",
224 | "beginCaptures": {
225 | "1": {
226 | "name": "punctuation.whitespace.comment.leading.graphql"
227 | }
228 | }
229 | }
230 | ]
231 | },
232 | "graphql-description-singleline": {
233 | "name": "comment.line.number-sign.graphql",
234 | "match": "#(?=([^\"]*\"[^\"]*\")*[^\"]*$).*$"
235 | },
236 | "graphql-description-docstring": {
237 | "name": "comment.block.graphql",
238 | "begin": "\"\"\"",
239 | "end": "\"\"\""
240 | },
241 | "graphql-variable-definitions": {
242 | "begin": "\\s*(\\()",
243 | "end": "\\s*(\\))",
244 | "captures": {
245 | "1": { "name": "meta.brace.round.graphql" }
246 | },
247 | "patterns": [
248 | { "include": "#graphql-comment" },
249 | { "include": "#graphql-description-docstring" },
250 | { "include": "#graphql-description-singleline" },
251 | { "include": "#graphql-variable-definition" },
252 | { "include": "#literal-quasi-embedded" }
253 | ]
254 | },
255 | "graphql-variable-definition": {
256 | "comment": "variable: type = value,.... which may be a list",
257 | "name": "meta.variables.graphql",
258 | "begin": "\\s*(\\$?[_A-Za-z][_0-9A-Za-z]*)(?=\\s*\\(|:)",
259 | "end": "(?=\\s*((\\$?[_A-Za-z][_0-9A-Za-z]*)\\s*(\\(|:)|(}|\\))))|\\s*(,)",
260 | "beginCaptures": {
261 | "1": { "name": "variable.parameter.graphql" }
262 | },
263 | "endCaptures": {
264 | "5": { "name": "punctuation.comma.graphql" }
265 | },
266 | "patterns": [
267 | { "include": "#graphql-comment" },
268 | { "include": "#graphql-description-docstring" },
269 | { "include": "#graphql-description-singleline" },
270 | { "include": "#graphql-directive" },
271 | { "include": "#graphql-colon" },
272 | { "include": "#graphql-input-types" },
273 | { "include": "#graphql-variable-assignment" },
274 | { "include": "#literal-quasi-embedded" },
275 | { "include": "#graphql-skip-newlines" }
276 | ]
277 | },
278 | "graphql-input-types": {
279 | "patterns": [
280 | { "include": "#graphql-scalar-type" },
281 | {
282 | "match": "\\s*([_A-Za-z][_0-9A-Za-z]*)(?:\\s*(!))?",
283 | "captures": {
284 | "1": { "name": "support.type.graphql" },
285 | "2": { "name": "keyword.operator.nulltype.graphql" }
286 | }
287 | },
288 | {
289 | "name": "meta.type.list.graphql",
290 | "begin": "\\s*(\\[)",
291 | "end": "\\s*(\\])(?:\\s*(!))?",
292 | "captures": {
293 | "1": { "name": "meta.brace.square.graphql" },
294 | "2": { "name": "keyword.operator.nulltype.graphql" }
295 | },
296 | "patterns": [
297 | { "include": "#graphql-comment" },
298 | { "include": "#graphql-description-docstring" },
299 | { "include": "#graphql-description-singleline" },
300 | { "include": "#graphql-input-types" },
301 | { "include": "#graphql-comma" },
302 | { "include": "#literal-quasi-embedded" }
303 | ]
304 | }
305 | ]
306 | },
307 | "graphql-scalar": {
308 | "match": "\\s*\\b(scalar)\\b\\s*([_A-Za-z][_0-9A-Za-z]*)",
309 | "captures": {
310 | "1": { "name": "keyword.scalar.graphql" },
311 | "2": { "name": "entity.scalar.graphql" }
312 | }
313 | },
314 | "graphql-scalar-type": {
315 | "match": "\\s*\\b(Int|Float|String|Boolean|ID)\\b(?:\\s*(!))?",
316 | "captures": {
317 | "1": { "name": "support.type.builtin.graphql" },
318 | "2": { "name": "keyword.operator.nulltype.graphql" }
319 | }
320 | },
321 | "graphql-variable-assignment": {
322 | "begin": "\\s(=)",
323 | "end": "(?=[\n,)])",
324 | "applyEndPatternLast": 1,
325 | "beginCaptures": {
326 | "1": { "name": "punctuation.assignment.graphql" }
327 | },
328 | "patterns": [{ "include": "#graphql-value" }]
329 | },
330 | "graphql-comma": {
331 | "match": "\\s*(,)",
332 | "captures": {
333 | "1": { "name": "punctuation.comma.graphql" }
334 | }
335 | },
336 | "graphql-ampersand": {
337 | "match": "\\s*(&)",
338 | "captures": {
339 | "1": { "name": "keyword.operator.logical.graphql" }
340 | }
341 | },
342 | "graphql-colon": {
343 | "match": "\\s*(:)",
344 | "captures": {
345 | "1": { "name": "punctuation.colon.graphql" }
346 | }
347 | },
348 | "graphql-union-mark": {
349 | "match": "\\s*(\\|)",
350 | "captures": {
351 | "1": { "name": "punctuation.union.graphql" }
352 | }
353 | },
354 | "graphql-name": {
355 | "match": "\\s*([_A-Za-z][_0-9A-Za-z]*)",
356 | "captures": {
357 | "1": { "name": "entity.name.function.graphql" }
358 | }
359 | },
360 | "graphql-directive": {
361 | "begin": "\\s*((@)\\s*([_A-Za-z][_0-9A-Za-z]*))",
362 | "end": "(?=.)",
363 | "applyEndPatternLast": 1,
364 | "beginCaptures": {
365 | "1": { "name": "entity.name.function.directive.graphql" }
366 | },
367 | "patterns": [
368 | { "include": "#graphql-comment" },
369 | { "include": "#graphql-description-docstring" },
370 | { "include": "#graphql-description-singleline" },
371 | { "include": "#graphql-arguments" },
372 | { "include": "#literal-quasi-embedded" },
373 | { "include": "#graphql-skip-newlines" }
374 | ]
375 | },
376 | "graphql-directive-definition": {
377 | "begin": "\\s*(\\bdirective\\b)\\s*(@[_A-Za-z][_0-9A-Za-z]*)",
378 | "end": "(?=.)",
379 | "applyEndPatternLast": 1,
380 | "beginCaptures": {
381 | "1": {
382 | "name": "keyword.directive.graphql"
383 | },
384 | "2": {
385 | "name": "entity.name.function.directive.graphql"
386 | },
387 | "3": { "name": "keyword.on.graphql" },
388 | "4": { "name": "support.type.graphql" }
389 | },
390 | "patterns": [
391 | {
392 | "include": "#graphql-variable-definitions"
393 | },
394 | {
395 | "begin": "\\s*(\\bon\\b)\\s*([_A-Za-z]*)",
396 | "end": "(?=.)",
397 | "applyEndPatternLast": 1,
398 | "beginCaptures": {
399 | "1": {
400 | "name": "keyword.on.graphql"
401 | },
402 | "2": {
403 | "name": "support.type.location.graphql"
404 | }
405 | },
406 | "patterns": [
407 | {
408 | "include": "#graphql-skip-newlines"
409 | },
410 | {
411 | "include": "#graphql-comment"
412 | },
413 | {
414 | "include": "#literal-quasi-embedded"
415 | },
416 | {
417 | "match": "\\s*(\\|)\\s*([_A-Za-z]*)",
418 | "captures": {
419 | "2": {
420 | "name": "support.type.location.graphql"
421 | }
422 | }
423 | }
424 | ]
425 | },
426 | {
427 | "include": "#graphql-skip-newlines"
428 | },
429 | {
430 | "include": "#graphql-comment"
431 | },
432 | {
433 | "include": "#literal-quasi-embedded"
434 | }
435 | ]
436 | },
437 | "graphql-selection-set": {
438 | "name": "meta.selectionset.graphql",
439 | "begin": "\\s*({)",
440 | "end": "\\s*(})",
441 | "beginCaptures": {
442 | "1": { "name": "punctuation.operation.graphql" }
443 | },
444 | "endCaptures": {
445 | "1": { "name": "punctuation.operation.graphql" }
446 | },
447 | "patterns": [
448 | { "include": "#graphql-comment" },
449 | { "include": "#graphql-description-docstring" },
450 | { "include": "#graphql-description-singleline" },
451 | { "include": "#graphql-field" },
452 | { "include": "#graphql-fragment-spread" },
453 | { "include": "#graphql-inline-fragment" },
454 | { "include": "#graphql-comma" },
455 | { "include": "#native-interpolation" },
456 | { "include": "#literal-quasi-embedded" }
457 | ]
458 | },
459 | "graphql-field": {
460 | "patterns": [
461 | {
462 | "match": "\\s*([_A-Za-z][_0-9A-Za-z]*)\\s*(:)",
463 | "captures": {
464 | "1": { "name": "string.unquoted.alias.graphql" },
465 | "2": { "name": "punctuation.colon.graphql" }
466 | }
467 | },
468 | {
469 | "match": "\\s*([_A-Za-z][_0-9A-Za-z]*)",
470 | "captures": {
471 | "1": { "name": "variable.graphql" }
472 | }
473 | },
474 | { "include": "#graphql-arguments" },
475 | { "include": "#graphql-directive" },
476 | { "include": "#graphql-selection-set" },
477 | { "include": "#literal-quasi-embedded" },
478 | { "include": "#graphql-skip-newlines" }
479 | ]
480 | },
481 | "graphql-fragment-spread": {
482 | "begin": "\\s*(\\.\\.\\.)\\s*(?!\\bon\\b)([_A-Za-z][_0-9A-Za-z]*)",
483 | "end": "(?=.)",
484 | "applyEndPatternLast": 1,
485 | "captures": {
486 | "1": { "name": "keyword.operator.spread.graphql" },
487 | "2": { "name": "variable.fragment.graphql" }
488 | },
489 | "patterns": [
490 | { "include": "#graphql-comment" },
491 | { "include": "#graphql-description-docstring" },
492 | { "include": "#graphql-description-singleline" },
493 | { "include": "#graphql-selection-set" },
494 | { "include": "#graphql-directive" },
495 | { "include": "#literal-quasi-embedded" },
496 | { "include": "#graphql-skip-newlines" }
497 | ]
498 | },
499 | "graphql-inline-fragment": {
500 | "begin": "\\s*(\\.\\.\\.)\\s*(?:(\\bon\\b)\\s*([_A-Za-z][_0-9A-Za-z]*))?",
501 | "end": "(?=.)",
502 | "applyEndPatternLast": 1,
503 | "captures": {
504 | "1": { "name": "keyword.operator.spread.graphql" },
505 | "2": { "name": "keyword.on.graphql" },
506 | "3": { "name": "support.type.graphql" }
507 | },
508 | "patterns": [
509 | { "include": "#graphql-comment" },
510 | { "include": "#graphql-description-docstring" },
511 | { "include": "#graphql-description-singleline" },
512 | { "include": "#graphql-selection-set" },
513 | { "include": "#graphql-directive" },
514 | { "include": "#graphql-skip-newlines" },
515 | { "include": "#literal-quasi-embedded" }
516 | ]
517 | },
518 | "graphql-arguments": {
519 | "name": "meta.arguments.graphql",
520 | "begin": "\\s*(\\()",
521 | "end": "\\s*(\\))",
522 | "beginCaptures": {
523 | "1": { "name": "meta.brace.round.directive.graphql" }
524 | },
525 | "endCaptures": {
526 | "1": { "name": "meta.brace.round.directive.graphql" }
527 | },
528 | "patterns": [
529 | { "include": "#graphql-comment" },
530 | { "include": "#graphql-description-docstring" },
531 | { "include": "#graphql-description-singleline" },
532 | {
533 | "begin": "\\s*([_A-Za-z][_0-9A-Za-z]*)(?:\\s*(:))",
534 | "end": "(?=\\s*(?:(?:([_A-Za-z][_0-9A-Za-z]*)\\s*(:))|\\)))|\\s*(,)",
535 | "beginCaptures": {
536 | "1": { "name": "variable.parameter.graphql" },
537 | "2": { "name": "punctuation.colon.graphql" }
538 | },
539 | "endCaptures": {
540 | "3": { "name": "punctuation.comma.graphql" }
541 | },
542 | "patterns": [
543 | { "include": "#graphql-comment" },
544 | { "include": "#graphql-description-docstring" },
545 | { "include": "#graphql-description-singleline" },
546 | { "include": "#graphql-directive" },
547 | { "include": "#graphql-value" },
548 | { "include": "#graphql-skip-newlines" }
549 | ]
550 | },
551 | { "include": "#literal-quasi-embedded" }
552 | ]
553 | },
554 | "graphql-variable-name": {
555 | "match": "\\s*(\\$[_A-Za-z][_0-9A-Za-z]*)",
556 | "captures": {
557 | "1": { "name": "variable.graphql" }
558 | }
559 | },
560 | "graphql-float-value": {
561 | "match": "\\s*(-?(0|[1-9][0-9]*)(\\.[0-9]+)?((e|E)(\\+|-)?[0-9]+)?)",
562 | "captures": {
563 | "1": { "name": "constant.numeric.float.graphql" }
564 | }
565 | },
566 | "graphql-boolean-value": {
567 | "match": "\\s*\\b(true|false)\\b",
568 | "captures": {
569 | "1": { "name": "constant.language.boolean.graphql" }
570 | }
571 | },
572 | "graphql-null-value": {
573 | "match": "\\s*\\b(null)\\b",
574 | "captures": {
575 | "1": { "name": "constant.language.null.graphql" }
576 | }
577 | },
578 | "graphql-string-value": {
579 | "contentName": "string.quoted.double.graphql",
580 | "begin": "\\s*+((\"))",
581 | "end": "\\s*+(?:((\"))|(\n))",
582 | "beginCaptures": {
583 | "1": { "name": "string.quoted.double.graphql" },
584 | "2": { "name": "punctuation.definition.string.begin.graphql" }
585 | },
586 | "endCaptures": {
587 | "1": { "name": "string.quoted.double.graphql" },
588 | "2": { "name": "punctuation.definition.string.end.graphql" },
589 | "3": { "name": "invalid.illegal.newline.graphql" }
590 | },
591 | "patterns": [
592 | { "include": "#graphql-string-content" },
593 | { "include": "#literal-quasi-embedded" }
594 | ]
595 | },
596 | "graphql-string-content": {
597 | "patterns": [
598 | {
599 | "name": "constant.character.escape.graphql",
600 | "match": "\\\\[/'\"\\\\nrtbf]"
601 | },
602 | {
603 | "name": "constant.character.escape.graphql",
604 | "match": "\\\\u([0-9a-fA-F]{4})"
605 | }
606 | ]
607 | },
608 | "graphql-enum": {
609 | "name": "meta.enum.graphql",
610 | "begin": "\\s*+\\b(enum)\\b\\s*([_A-Za-z][_0-9A-Za-z]*)",
611 | "end": "(?<=})",
612 | "beginCaptures": {
613 | "1": { "name": "keyword.enum.graphql" },
614 | "2": { "name": "support.type.enum.graphql" }
615 | },
616 | "patterns": [
617 | {
618 | "name": "meta.type.object.graphql",
619 | "begin": "\\s*({)",
620 | "end": "\\s*(})",
621 | "beginCaptures": {
622 | "1": { "name": "punctuation.operation.graphql" }
623 | },
624 | "endCaptures": {
625 | "1": { "name": "punctuation.operation.graphql" }
626 | },
627 | "patterns": [
628 | { "include": "#graphql-object-type" },
629 |
630 | { "include": "#graphql-comment" },
631 | { "include": "#graphql-description-docstring" },
632 | { "include": "#graphql-description-singleline" },
633 | { "include": "#graphql-directive" },
634 | { "include": "#graphql-enum-value" },
635 | { "include": "#literal-quasi-embedded" }
636 | ]
637 | },
638 | { "include": "#graphql-comment" },
639 | { "include": "#graphql-description-docstring" },
640 | { "include": "#graphql-description-singleline" },
641 | { "include": "#graphql-directive" }
642 | ]
643 | },
644 | "graphql-enum-value": {
645 | "name": "constant.character.enum.graphql",
646 | "match": "\\s*(?!=\\b(true|false|null)\\b)([_A-Za-z][_0-9A-Za-z]*)"
647 | },
648 | "graphql-value": {
649 | "patterns": [
650 | { "include": "#graphql-comment" },
651 | { "include": "#graphql-description-docstring" },
652 | { "include": "#graphql-variable-name" },
653 | { "include": "#graphql-float-value" },
654 | { "include": "#graphql-string-value" },
655 | { "include": "#graphql-boolean-value" },
656 | { "include": "#graphql-null-value" },
657 | { "include": "#graphql-enum-value" },
658 | { "include": "#graphql-list-value" },
659 | { "include": "#graphql-object-value" },
660 | { "include": "#literal-quasi-embedded" }
661 | ]
662 | },
663 | "graphql-list-value": {
664 | "patterns": [
665 | {
666 | "name": "meta.listvalues.graphql",
667 | "begin": "\\s*+(\\[)",
668 | "end": "\\s*(\\])",
669 | "endCaptures": {
670 | "1": { "name": "meta.brace.square.graphql" }
671 | },
672 | "beginCaptures": {
673 | "1": { "name": "meta.brace.square.graphql" }
674 | },
675 | "patterns": [{ "include": "#graphql-value" }]
676 | }
677 | ]
678 | },
679 | "graphql-object-value": {
680 | "patterns": [
681 | {
682 | "name": "meta.objectvalues.graphql",
683 | "begin": "\\s*+({)",
684 | "end": "\\s*(})",
685 | "beginCaptures": {
686 | "1": { "name": "meta.brace.curly.graphql" }
687 | },
688 | "endCaptures": {
689 | "1": { "name": "meta.brace.curly.graphql" }
690 | },
691 | "patterns": [
692 | { "include": "#graphql-object-field" },
693 | { "include": "#graphql-value" }
694 | ]
695 | }
696 | ]
697 | },
698 | "graphql-object-field": {
699 | "match": "\\s*(([_A-Za-z][_0-9A-Za-z]*))\\s*(:)",
700 | "captures": {
701 | "1": { "name": "constant.object.key.graphql" },
702 | "2": { "name": "string.unquoted.graphql" },
703 | "3": { "name": "punctuation.graphql" }
704 | }
705 | },
706 | "graphql-union": {
707 | "begin": "\\s*\\b(union)\\b\\s*([_A-Za-z][_0-9A-Za-z]*)",
708 | "end": "(?=.)",
709 | "applyEndPatternLast": 1,
710 | "captures": {
711 | "1": { "name": "keyword.union.graphql" },
712 | "2": { "name": "support.type.graphql" }
713 | },
714 | "patterns": [
715 | {
716 | "begin": "\\s*(=)\\s*([_A-Za-z][_0-9A-Za-z]*)",
717 | "end": "(?=.)",
718 | "applyEndPatternLast": 1,
719 | "captures": {
720 | "1": { "name": "punctuation.assignment.graphql" },
721 | "2": { "name": "support.type.graphql" }
722 | },
723 | "patterns": [
724 | { "include": "#graphql-comment" },
725 | { "include": "#graphql-description-docstring" },
726 | { "include": "#graphql-description-singleline" },
727 | { "include": "#graphql-skip-newlines" },
728 | { "include": "#literal-quasi-embedded" },
729 | {
730 | "match": "\\s*(\\|)\\s*([_A-Za-z][_0-9A-Za-z]*)",
731 | "captures": {
732 | "1": { "name": "punctuation.or.graphql" },
733 | "2": { "name": "support.type.graphql" }
734 | }
735 | }
736 | ]
737 | },
738 | { "include": "#graphql-comment" },
739 | { "include": "#graphql-description-docstring" },
740 | { "include": "#graphql-description-singleline" },
741 | { "include": "#graphql-skip-newlines" },
742 | { "include": "#literal-quasi-embedded" }
743 | ]
744 | },
745 | "native-interpolation": {
746 | "name": "native.interpolation",
747 | "begin": "\\s*(\\${)",
748 | "end": "(})",
749 | "beginCaptures": {
750 | "1": { "name": "keyword.other.substitution.begin" }
751 | },
752 | "endCaptures": {
753 | "1": { "name": "keyword.other.substitution.end" }
754 | },
755 | "patterns": [
756 | { "include": "source.js" },
757 | { "include": "source.ts" },
758 | { "include": "source.js.jsx" },
759 | { "include": "source.tsx" }
760 | ]
761 | },
762 | "graphql-skip-newlines": {
763 | "match": "\\s*\n"
764 | }
765 | }
766 | }
767 |
--------------------------------------------------------------------------------
/images/github-graphql.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joyceerhl/vscode-github-graphql-notebooks/e2210c2a4ad869208e0d6d60571c5d144246cd7b/images/github-graphql.gif
--------------------------------------------------------------------------------
/language/language-configuration.json:
--------------------------------------------------------------------------------
1 | {
2 | "comments": {
3 | "lineComment": "#",
4 | "blockComment": ["\"\"\"", "\"\"\""]
5 | },
6 | "brackets": [["{", "}"], ["[", "]"], ["(", ")"]],
7 | "autoClosingPairs": [
8 | ["{", "}"],
9 | ["[", "]"],
10 | ["(", ")"],
11 | { "open": "\"", "close": "\"", "notIn": ["string", "comment"] },
12 | ["'", "'"]
13 | ],
14 | "surroundingPairs": [
15 | ["{", "}"],
16 | ["[", "]"],
17 | ["(", ")"],
18 | ["\"", "\""],
19 | ["'", "'"]
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "github-graphql-nb",
3 | "displayName": "GitHub GraphQL Notebooks",
4 | "publisher": "joyceerhl",
5 | "description": "Interactively run GitHub GraphQL queries and mutations in VS Code",
6 | "repository": {
7 | "url": "https://github.com/joyceerhl/vscode-github-graphql-notebooks"
8 | },
9 | "version": "0.2.0",
10 | "license": "MIT",
11 | "engines": {
12 | "vscode": "^1.64.0"
13 | },
14 | "categories": [
15 | "Notebooks"
16 | ],
17 | "keywords": [
18 | "GitHub",
19 | "GraphQL"
20 | ],
21 | "extensionDependencies": [
22 | "joyceerhl.vscode-graphql-notebook"
23 | ],
24 | "activationEvents": [
25 | "onNotebook:gqlnb",
26 | "onNotebook:github-graphql-nb"
27 | ],
28 | "browser": "./dist/browser/extension",
29 | "main": "./dist/extension",
30 | "contributes": {
31 | "configuration": {
32 | "title": "GitHub GraphQL Notebooks",
33 | "properties": {
34 | "githubGraphql.scopes": {
35 | "type": "array",
36 | "items": {
37 | "type": "string"
38 | },
39 | "default": [
40 | "repo",
41 | "workflow"
42 | ],
43 | "markdownDescription": "The authentication scopes to request when signing into GitHub. See [Available Scopes](https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps#available-scopes) in GitHub Documentation for more information.",
44 | "source": "resource"
45 | }
46 | }
47 | },
48 | "languages": [
49 | {
50 | "id": "graphql",
51 | "aliases": [
52 | "GraphQL",
53 | "graphql"
54 | ],
55 | "configuration": "./language/language-configuration.json"
56 | }
57 | ],
58 | "grammars": [
59 | {
60 | "language": "graphql",
61 | "path": "./grammars/graphql.json",
62 | "scopeName": "source.graphql"
63 | }
64 | ]
65 | },
66 | "scripts": {
67 | "vscode:prepublish": "npm run package",
68 | "compile": "webpack",
69 | "watch": "webpack --watch",
70 | "package": "webpack --mode production --devtool hidden-source-map",
71 | "test-compile": "tsc -p ./",
72 | "test-watch": "tsc -watch -p ./",
73 | "pretest": "npm run test-compile && npm run lint",
74 | "lint": "eslint src --ext ts",
75 | "test": "node ./out/test/runTest.js",
76 | "serve": "npx serve --cors -l 5000",
77 | "tunnel": "npx localtunnel -p 5000"
78 | },
79 | "dependencies": {
80 | "@octokit/core": "3.5.1",
81 | "cross-fetch": "3.1.5",
82 | "node-fetch": "3.2.3",
83 | "vsce": "^2.7.0"
84 | },
85 | "devDependencies": {
86 | "@types/glob": "^7.1.3",
87 | "@types/mocha": "^8.2.2",
88 | "@types/node": "14.x",
89 | "@types/vscode": "^1.60.0",
90 | "@typescript-eslint/eslint-plugin": "^4.26.0",
91 | "@typescript-eslint/parser": "^4.26.0",
92 | "eslint": "^7.27.0",
93 | "glob": "^7.1.7",
94 | "mocha": "^9.1.4",
95 | "ts-loader": "^9.2.2",
96 | "typescript": "^4.3.2",
97 | "vscode-test": "^1.5.2",
98 | "webpack": "^5.38.1",
99 | "webpack-cli": "^4.7.0"
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/extension.ts:
--------------------------------------------------------------------------------
1 | import { Octokit } from '@octokit/core';
2 | import fetch from 'node-fetch';
3 | import * as vscode from 'vscode';
4 | import { authentication, AuthenticationSession } from 'vscode';
5 |
6 | export function activate(context: vscode.ExtensionContext) {
7 | context.subscriptions.push(new OctokitController());
8 | }
9 |
10 | // this method is called when your extension is deactivated
11 | export function deactivate() {}
12 |
13 |
14 | var authorizationScopes = vscode.workspace.getConfiguration('githubGraphql').get('scopes') as string[];
15 |
16 | const variablesRegex = /^\s*variables\s*(\{[^}]*\})\s*$/m;
17 |
18 | class OctokitController {
19 | private _octokit: Octokit | undefined;
20 | private _octokitDefaults: typeof Octokit | undefined;
21 | private _session: AuthenticationSession | undefined;
22 | private controller: vscode.NotebookController;
23 |
24 | constructor() {
25 | this.controller = vscode.notebooks.createNotebookController('github-graphql', 'gqlnb', 'GitHub GraphQL', (cells, notebook, c) => this.executeCells(cells, notebook, c));
26 | vscode.workspace.onDidChangeConfiguration((e) => {
27 | if (e.affectsConfiguration('githubGraphql')) {
28 | authorizationScopes = vscode.workspace.getConfiguration('githubGraphql').get('scopes') ?? []; // Update our cached auth scopes
29 | this.clearAuthenticationSession(); // Clear out the existing auth session if requested scopes changed
30 | }
31 | });
32 | }
33 |
34 | dispose() {
35 | return this.controller.dispose();
36 | }
37 |
38 | private async executeCells(cells: vscode.NotebookCell[], notebook: vscode.NotebookDocument, controller: vscode.NotebookController) {
39 | for (const cell of cells) {
40 | await this.executeCell(cell);
41 | }
42 | }
43 |
44 | private async executeCell(cell: vscode.NotebookCell) {
45 | const task = this.controller.createNotebookCellExecution(cell);
46 | task.start(Date.now());
47 |
48 | const contents = cell.document.getText();
49 |
50 | let code: string;
51 | let variables: Record | undefined;
52 | const match = contents.match(variablesRegex);
53 | if (match) {
54 | try {
55 | variables = JSON.parse(match[1]);
56 | } catch (e) {
57 | replaceOutput(task, `Unable to parse 'variables': ${String(e)}`);
58 | task.end(false, Date.now());
59 | return;
60 | }
61 |
62 | code = contents.replace(variablesRegex, '').trim();
63 | } else {
64 | code = contents.trim();
65 | }
66 |
67 | let success = false;
68 | try {
69 | const resp = await this.graphql(code, variables);
70 | success = true;
71 | replaceOutput(task, resp);
72 | } catch (e) {
73 | replaceOutput(task, e);
74 | }
75 | task.end(success, Date.now());
76 | }
77 |
78 | private async graphql(query: string, variables?: Record): Promise {
79 | if (this._octokit === undefined) {
80 | this._octokit = await this.octokit();
81 | }
82 |
83 | try {
84 | return await this._octokit.graphql(query, variables);
85 | } catch (ex) {
86 | throw ex;
87 | }
88 | }
89 |
90 | private async octokit(options?: ConstructorParameters[0]) {
91 | if (this._octokitDefaults === undefined) {
92 | const session = await this.ensureAuthenticated();
93 |
94 | if (vscode.env.uiKind === vscode.UIKind.Web) {
95 | async function fetchCore(url: string, options: { headers?: Record }) {
96 | if (options.headers !== undefined) {
97 | const { 'user-agent': userAgent, ...headers } = options.headers;
98 | if (userAgent) {
99 | options.headers = headers;
100 | }
101 | }
102 | return fetch(url, options);
103 | }
104 |
105 | this._octokitDefaults = Octokit.defaults({
106 | auth: `token ${session.accessToken}`,
107 | request: { fetch: fetchCore },
108 | });
109 | } else {
110 | this._octokitDefaults = Octokit.defaults({ auth: `token ${session.accessToken}` });
111 | }
112 | }
113 |
114 | const octokit = new this._octokitDefaults(options);
115 | return octokit;
116 | }
117 |
118 | private async ensureAuthenticated(force: boolean = false) {
119 | if (this._session === undefined) {
120 | async function waitUntilAuthenticated() {
121 | try {
122 | const session = await authentication.getSession('github', authorizationScopes as readonly string[], {
123 | createIfNone: true,
124 | });
125 | if (session !== undefined) {
126 | return session;
127 | }
128 | } catch {}
129 |
130 | return new Promise(resolve => {
131 | async function getSession() {
132 | const session = await authentication.getSession('github', authorizationScopes as readonly string[], {
133 | createIfNone: true,
134 | });
135 | if (session !== undefined) {
136 | resolve(session);
137 | }
138 | }
139 |
140 | const disposable = authentication.onDidChangeSessions(async e => {
141 | if (e.provider.id === 'github') {
142 | disposable.dispose();
143 | await getSession();
144 | }
145 | });
146 | });
147 | }
148 |
149 | this._session = await waitUntilAuthenticated();
150 | const disposable = authentication.onDidChangeSessions(e => {
151 | if (this._session === undefined) {
152 | disposable.dispose();
153 | return;
154 | }
155 |
156 | if (e.provider.id === 'github') {
157 | this.clearAuthenticationSession();
158 | disposable.dispose();
159 | }
160 | });
161 | }
162 |
163 | return this._session;
164 | }
165 |
166 | private clearAuthenticationSession() {
167 | this._session = undefined;
168 | this._octokit = undefined;
169 | this._octokitDefaults = undefined;
170 | }
171 | }
172 |
173 |
174 | function replaceOutput(task: vscode.NotebookCellExecution, jsonData: unknown) {
175 | const stringified = JSON.stringify(jsonData, undefined, 4);
176 | let data;
177 | if (vscode.env.uiKind === vscode.UIKind.Web) {
178 | data = new TextEncoder().encode(stringified);
179 | } else {
180 | data = Buffer.from(stringified);
181 | }
182 | const item = new vscode.NotebookCellOutputItem(data, 'text/x-json');
183 | const output = new vscode.NotebookCellOutput([item]);
184 | task.replaceOutput(output);
185 | }
186 |
--------------------------------------------------------------------------------
/src/test/runTest.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 |
3 | import { runTests } from 'vscode-test';
4 |
5 | async function main() {
6 | try {
7 | // The folder containing the Extension Manifest package.json
8 | // Passed to `--extensionDevelopmentPath`
9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../');
10 |
11 | // The path to test runner
12 | // Passed to --extensionTestsPath
13 | const extensionTestsPath = path.resolve(__dirname, './suite/index');
14 |
15 | // Download VS Code, unzip it and run the integration test
16 | await runTests({ extensionDevelopmentPath, extensionTestsPath });
17 | } catch (err) {
18 | console.error('Failed to run tests');
19 | process.exit(1);
20 | }
21 | }
22 |
23 | main();
24 |
--------------------------------------------------------------------------------
/src/test/suite/extension.test.ts:
--------------------------------------------------------------------------------
1 | import * as assert from 'assert';
2 |
3 | // You can import and use all API from the 'vscode' module
4 | // as well as import your extension to test it
5 | import * as vscode from 'vscode';
6 | // import * as myExtension from '../../extension';
7 |
8 | suite('Extension Test Suite', () => {
9 | vscode.window.showInformationMessage('Start all tests.');
10 |
11 | test('Sample test', () => {
12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5));
13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0));
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/test/suite/index.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import * as Mocha from 'mocha';
3 | import * as glob from 'glob';
4 |
5 | export function run(): Promise {
6 | // Create the mocha test
7 | const mocha = new Mocha({
8 | ui: 'tdd',
9 | color: true
10 | });
11 |
12 | const testsRoot = path.resolve(__dirname, '..');
13 |
14 | return new Promise((c, e) => {
15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
16 | if (err) {
17 | return e(err);
18 | }
19 |
20 | // Add files to the test suite
21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
22 |
23 | try {
24 | // Run the mocha test
25 | mocha.run(failures => {
26 | if (failures > 0) {
27 | e(new Error(`${failures} tests failed.`));
28 | } else {
29 | c();
30 | }
31 | });
32 | } catch (err) {
33 | console.error(err);
34 | e(err);
35 | }
36 | });
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es6",
5 | "outDir": "out",
6 | "lib": [
7 | "es6"
8 | ],
9 | "sourceMap": true,
10 | "rootDir": "src",
11 | "strict": true /* enable all strict type-checking options */
12 | /* Additional Checks */
13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
16 | },
17 | "exclude": [
18 | "node_modules",
19 | ".vscode-test"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/vsc-extension-quickstart.md:
--------------------------------------------------------------------------------
1 | # Welcome to your VS Code Extension
2 |
3 | ## What's in the folder
4 |
5 | * This folder contains all of the files necessary for your extension.
6 | * `package.json` - this is the manifest file in which you declare your extension and command.
7 | * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command.
9 | * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
10 | * We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
11 |
12 | ## Get up and running straight away
13 |
14 | * Press `F5` to open a new window with your extension loaded.
15 | * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
16 | * Set breakpoints in your code inside `src/extension.ts` to debug your extension.
17 | * Find output from your extension in the debug console.
18 |
19 | ## Make changes
20 |
21 | * You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
22 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
23 |
24 |
25 | ## Explore the API
26 |
27 | * You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
28 |
29 | ## Run tests
30 |
31 | * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`.
32 | * Press `F5` to run the tests in a new window with your extension loaded.
33 | * See the output of the test result in the debug console.
34 | * Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder.
35 | * The provided test runner will only consider files matching the name pattern `**.test.ts`.
36 | * You can create folders inside the `test` folder to structure your tests any way you want.
37 |
38 | ## Go further
39 |
40 | * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
41 | * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace.
42 | * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
43 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | //@ts-check
2 | /** @typedef {import('webpack').Configuration} WebpackConfig **/
3 |
4 | 'use strict';
5 |
6 | const path = require('path');
7 |
8 | async function getExtensionConfig(target, mode, env) {
9 | return {
10 | name: `extension:${target}`,
11 | entry: './src/extension.ts',
12 | target: target,
13 | mode: mode,
14 | devtool: 'nosources-source-map',
15 | externals: {
16 | vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
17 | // modules added here also need to be added in the .vsceignore file
18 | },
19 | output: {
20 | path: target === 'webworker' ? path.join(__dirname, 'dist', 'browser') : path.join(__dirname, 'dist'),
21 | filename: 'extension.js',
22 | libraryTarget: 'commonjs2'
23 | },
24 | resolve: {
25 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
26 | extensions: ['.ts', '.js'],
27 | alias:
28 | target === 'webworker' ? { 'node-fetch': 'cross-fetch' } : undefined,
29 | },
30 | module: {
31 | rules: [
32 | {
33 | test: /\.ts$/,
34 | exclude: /node_modules/,
35 | use: [
36 | {
37 | loader: 'ts-loader'
38 | }
39 | ]
40 | }
41 | ]
42 | }
43 | };
44 | }
45 |
46 | module.exports = /**
47 | * @param {{ esbuild?: boolean; } | undefined } env
48 | * @param {{ mode: 'production' | 'development' | 'none' | undefined; }} argv
49 | * @returns { Promise }
50 | */
51 | async function (env, argv) {
52 | const mode = argv.mode || 'none';
53 |
54 | env = {
55 | esbuild: false,
56 | ...env,
57 | };
58 |
59 | return Promise.all([
60 | getExtensionConfig('node', mode, env),
61 | getExtensionConfig('webworker', mode, env),
62 | ]);
63 | };
64 |
--------------------------------------------------------------------------------