Skip to content

Commit 2bed8b1

Browse files
authored
Improve Extension Authoring Experience with new Build Tasks and Launch Configs (#4510)
Updates the build tasks and launch configs to: - Massively shorten inner loop development on typescript features by using incremental watch configs - Integrate linting process into vscode so that lint errors are surfaced in the IDE prior to CI commit or manual lint run - Enable semicolons for typescript actions like auto-formatting since the current ESLint profile requires them. - Provide a launch test task for isolated testing separate from developer's profile - Provide extension launchers for interactive debugging of the extension in either the developer's profile, a temp throwaway profile, or an isolated but persistent profile
1 parent 707febd commit 2bed8b1

11 files changed

+248
-64
lines changed

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ obj/
77
bin/
88
out/
99
sessions/
10-
test/.vscode/
11-
1210
test-results.xml
1311
*.vsix
1412
*.DS_Store

examples/.vscode/settings.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,4 @@
33
// Relative paths for this setting are always relative to the workspace root dir.
44
"powershell.scriptAnalysis.settingsPath": "./PSScriptAnalyzerSettings.psd1",
55
"files.defaultLanguage": "powershell",
6-
// Suppresses some first-run messages
7-
"git.openRepositoryInParentFolders": "never",
8-
"csharp.suppressDotnetRestoreNotification": true,
9-
"extensions.ignoreRecommendations": true
106
}

extension-dev.code-workspace

Lines changed: 194 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
]
2222
},
2323
"settings": {
24+
"window.title": "PowerShell VS Code Extension Development",
25+
"debug.onTaskErrors": "prompt",
2426
"editor.tabSize": 4,
2527
"editor.insertSpaces": true,
2628
"files.trimTrailingWhitespace": true,
@@ -44,7 +46,17 @@
4446
"powershell.codeFormatting.whitespaceBetweenParameters": true,
4547
"powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline",
4648
// Lock the TypeScript SDK path to the version we use
47-
"typescript.tsdk": "Client/node_modules/typescript/lib"
49+
"typescript.tsdk": "Client/node_modules/typescript/lib",
50+
// Code actions like "organize imports" ignore ESLint, so we need this here
51+
"typescript.format.semicolons": "insert",
52+
// Enable ESLint as defaut formatter so quick fixes can be applied directly
53+
"eslint.format.enable": true,
54+
"[typescript]": {
55+
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
56+
"editor.formatOnPaste": true,
57+
"editor.formatOnSave": true,
58+
"editor.formatOnSaveMode": "modificationsIfAvailable"
59+
}
4860
},
4961
"tasks": {
5062
"version": "2.0.0",
@@ -90,7 +102,7 @@
90102
"options": {
91103
"cwd": "${workspaceFolder:Client}"
92104
},
93-
"command": "Invoke-Build Build",
105+
"command": "./build.ps1",
94106
"problemMatcher": [
95107
"$msCompile",
96108
"$tsc"
@@ -106,7 +118,7 @@
106118
"options": {
107119
"cwd": "${workspaceFolder:Client}"
108120
},
109-
"command": "Invoke-Build Test",
121+
"command": "./build.ps1 -Test",
110122
"problemMatcher": [
111123
"$msCompile",
112124
"$tsc"
@@ -148,7 +160,39 @@
148160
},
149161
"command": "Invoke-Build ${input:serverBuildCommand}",
150162
"group": "build"
151-
}
163+
},
164+
// HACK: Can't use task type npm in workspace config: https://github.com/microsoft/vscode/issues/96086
165+
{
166+
"label": "test-watch",
167+
"icon": {
168+
"color": "terminal.ansiCyan",
169+
"id": "sync"
170+
},
171+
"type": "shell",
172+
"options": {
173+
"cwd": "${workspaceFolder:Client}"
174+
},
175+
"command": "npm run-script build-test-watch",
176+
"group": "test",
177+
"problemMatcher": "$tsc-watch",
178+
"isBackground": true,
179+
"dependsOn": "build-watch" // We need to also build main.js extension for testing or it leads to sourcemap errors
180+
},
181+
{
182+
"label": "build-watch",
183+
"icon": {
184+
"color": "terminal.ansiCyan",
185+
"id": "sync"
186+
},
187+
"type": "shell",
188+
"options": {
189+
"cwd": "${workspaceFolder:Client}"
190+
},
191+
"command": "npm run-script build-watch",
192+
"group": "build",
193+
"problemMatcher": "$esbuild-watch",
194+
"isBackground": true,
195+
},
152196
],
153197
"inputs": [
154198
{
@@ -184,7 +228,52 @@
184228
},
185229
"launch": {
186230
"version": "0.2.0",
231+
"compounds": [
232+
{
233+
"name": "Test Extension",
234+
"configurations": [
235+
"ExtensionTests",
236+
"ExtensionTestRunner",
237+
],
238+
"stopAll": true,
239+
"presentation": {
240+
"group": "test",
241+
"order": 1
242+
},
243+
// This is here so instead of under TestRunner so that the attach doesn't start until the compile is complete
244+
"preLaunchTask": "test-watch"
245+
}
246+
],
187247
"configurations": [
248+
{
249+
"name": "Launch Extension",
250+
"type": "extensionHost",
251+
"request": "launch",
252+
"runtimeExecutable": "${execPath}",
253+
"args": [
254+
"--extensionDevelopmentPath=${workspaceFolder:Client}"
255+
],
256+
"env": {
257+
"__TEST_WORKSPACE_PATH": "${workspaceFolder:Client}/examples",
258+
},
259+
"sourceMaps": true,
260+
// This speeds up source map detection and makes smartStep work correctly
261+
"outFiles": [
262+
"${workspaceFolder:Client}/out/**/*.js",
263+
"!**/node_modules/**",
264+
"!**/.vscode-test/**"
265+
],
266+
"skipFiles": [
267+
"<node_internals>/**",
268+
"**/node_modules/**",
269+
"**/.vscode-test/**"
270+
],
271+
"presentation": {
272+
"hidden": false,
273+
"group": "test",
274+
"order": 2
275+
}
276+
},
188277
{
189278
// https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
190279
"name": "Attach to Editor Services",
@@ -197,42 +286,129 @@
197286
"searchPaths": [],
198287
"searchMicrosoftSymbolServer": true,
199288
"searchNuGetOrgSymbolServer": true
289+
},
290+
"presentation": {
291+
"hidden": false,
292+
"group": "test",
293+
"order": 3
200294
}
201295
},
202296
{
203-
"name": "Launch Extension",
297+
// Runs the extension in an empty temp profile that is automatically cleaned up after use
298+
// Undocumented: https://github.com/microsoft/vscode-docs/issues/6220
299+
"name": "Launch Extension - Temp Profile",
204300
"type": "extensionHost",
205301
"request": "launch",
206302
"runtimeExecutable": "${execPath}",
207303
"args": [
208-
"--disable-extensions",
209-
"--extensionDevelopmentPath=${workspaceFolder:Client}"
304+
"--profile-temp",
305+
"--extensionDevelopmentPath=${workspaceFolder:Client}",
306+
"${workspaceFolder:Client}/examples"
210307
],
211308
"sourceMaps": true,
309+
// This speeds up source map detection and makes smartStep work correctly
212310
"outFiles": [
213-
"${workspaceFolder:Client}/out/main.js"
311+
"${workspaceFolder:Client}/out/**/*.js",
312+
"!**/node_modules/**",
313+
"!**/.vscode-test/**"
314+
],
315+
"skipFiles": [
316+
"<node_internals>/**",
317+
"**/node_modules/**",
318+
"**/.vscode-test/**"
214319
],
215-
"preLaunchTask": "${defaultBuildTask}",
320+
"presentation": {
321+
"hidden": false,
322+
"group": "test",
323+
"order": 2
324+
}
216325
},
217326
{
218-
"name": "Launch Extension Tests",
327+
// Runs the extension in an isolated but persistent profile separate from the user settings
328+
// Undocumented: https://github.com/microsoft/vscode-docs/issues/6220
329+
"name": "Launch Extension - Isolated Profile",
219330
"type": "extensionHost",
220331
"request": "launch",
221332
"runtimeExecutable": "${execPath}",
222333
"args": [
223-
// The tests require Code be opened with a workspace, which exists in
224-
// `test`, but this has to be passed as a CLI argument, not just `cwd`.
225-
"${workspaceFolder:Client}/test",
226-
"--disableExtensions",
334+
"--profile=debug",
227335
"--extensionDevelopmentPath=${workspaceFolder:Client}",
228-
"--extensionTestsPath=${workspaceFolder:Client}/out/test/index.js",
336+
"${workspaceFolder:Client}/examples"
229337
],
230338
"sourceMaps": true,
339+
// This speeds up source map detection and makes smartStep work correctly
231340
"outFiles": [
232-
"${workspaceFolder:Client}/out/test/**/*.js"
341+
"${workspaceFolder:Client}/out/**/*.js",
342+
"!**/node_modules/**",
343+
"!**/.vscode-test/**"
344+
],
345+
"skipFiles": [
346+
"<node_internals>/**",
347+
"**/node_modules/**",
348+
"**/.vscode-test/**"
349+
],
350+
"presentation": {
351+
"hidden": false,
352+
"group": "test",
353+
"order": 2
354+
}
355+
},
356+
{
357+
"name": "ExtensionTestRunner",
358+
"type": "node",
359+
"request": "launch",
360+
"program": "${workspaceFolder:Client}/out/test/runTests.js",
361+
"cascadeTerminateToConfigurations": [
362+
"ExtensionTests",
363+
],
364+
// This speeds up source map detection and makes smartStep work correctly
365+
"outFiles": [
366+
"${workspaceFolder:Client}/out/**/*.js",
367+
"!**/node_modules/**",
368+
"!**/.vscode-test/**"
369+
],
370+
"skipFiles": [
371+
"<node_internals>/**",
372+
"**/node_modules/**",
373+
"**/.vscode-test/**"
374+
],
375+
"args": [
376+
"59229" // Wait on this port for the separate debugger task to attach
377+
],
378+
"presentation": {
379+
"hidden": true,
380+
},
381+
"internalConsoleOptions": "neverOpen",
382+
"console": "integratedTerminal",
383+
"autoAttachChildProcesses": false // Doesnt work with the extension host for whatever reason, hence the separate attach.
384+
},
385+
{
386+
"name": "ExtensionTests",
387+
"type": "node",
388+
"request": "attach",
389+
"port": 59229,
390+
"autoAttachChildProcesses": true,
391+
"outputCapture": "console",
392+
"continueOnAttach": true,
393+
// Sometimes we may need to install extensions or reload the window which requires reconnecting
394+
"restart": {
395+
"delay": 1000,
396+
"maxAttempts": 3
397+
},
398+
"presentation": {
399+
"hidden": true,
400+
},
401+
// This speeds up source map detection and makes smartStep work correctly
402+
"outFiles": [
403+
"${workspaceFolder:Client}/out/**/*.js",
404+
"!**/node_modules/**",
405+
"!**/.vscode-test/**"
406+
],
407+
"skipFiles": [
408+
"<node_internals>/**",
409+
"**/node_modules/**",
410+
"**/.vscode-test/**"
233411
],
234-
"preLaunchTask": "${defaultBuildTask}",
235-
"internalConsoleOptions": "openOnSessionStart"
236412
}
237413
]
238414
}

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,11 @@
115115
"main": "./out/main.js",
116116
"scripts": {
117117
"lint": "eslint . --ext .ts",
118-
"build": "tsc --project tsconfig.json && esbuild ./src/main.ts --outdir=out --bundle --external:vscode --platform=node",
119-
"test": "node ./out/test/runTests.js",
118+
"build": "esbuild ./src/main.ts --outdir=out --bundle --external:vscode --platform=node",
119+
"build-watch": "npm run build -- --watch",
120+
"build-test": "tsc --incremental",
121+
"build-test-watch": "npm run build-test -- --watch",
122+
"test": "npm run build-test && node ./out/test/runTests.js",
120123
"package": "vsce package --no-gitHubIssueLinking",
121124
"publish": "vsce publish"
122125
},

test/.vscode/settings.json

Lines changed: 0 additions & 7 deletions
This file was deleted.

test/TestEnvironment.code-workspace

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
// A simple test environment that suppresses some first start warnings we don't care about.
3+
"folders": [
4+
{
5+
"path": "mocks"
6+
}
7+
],
8+
"settings": {
9+
"git.openRepositoryInParentFolders": "never",
10+
"csharp.suppressDotnetRestoreNotification": true,
11+
"extensions.ignoreRecommendations": true
12+
}
13+
}

test/core/settings.test.ts

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,32 @@
33

44
import * as assert from "assert";
55
import * as vscode from "vscode";
6-
import * as settings from "../../src/settings";
6+
import { Settings, getSettings, getEffectiveConfigurationTarget, changeSetting, CommentType } from "../../src/settings";
77

8-
describe("Settings module", function () {
8+
describe("Settings E2E", function () {
9+
this.slow(800);
910
it("Loads without error", function () {
10-
assert.doesNotThrow(settings.getSettings);
11+
assert.doesNotThrow(getSettings);
1112
});
1213

1314
it("Loads the correct defaults", function () {
14-
const testSettings = new settings.Settings();
15-
testSettings.enableProfileLoading = false;
16-
testSettings.powerShellAdditionalExePaths = { "Some PowerShell": "somePath" };
17-
const actualSettings = settings.getSettings();
15+
const testSettings = new Settings();
16+
const actualSettings = getSettings();
1817
assert.deepStrictEqual(actualSettings, testSettings);
1918
});
2019

21-
2220
it("Updates correctly", async function () {
23-
await settings.changeSetting("helpCompletion", settings.CommentType.LineComment, false, undefined);
24-
assert.strictEqual(settings.getSettings().helpCompletion, settings.CommentType.LineComment);
21+
await changeSetting("helpCompletion", CommentType.LineComment, false, undefined);
22+
assert.strictEqual(getSettings().helpCompletion, CommentType.LineComment);
2523
});
2624

2725
it("Gets the effective configuration target", async function () {
28-
await settings.changeSetting("helpCompletion", settings.CommentType.LineComment, false, undefined);
29-
let target = settings.getEffectiveConfigurationTarget("helpCompletion");
26+
await changeSetting("helpCompletion", CommentType.LineComment, false, undefined);
27+
let target = getEffectiveConfigurationTarget("helpCompletion");
3028
assert.strictEqual(target, vscode.ConfigurationTarget.Workspace);
3129

32-
await settings.changeSetting("helpCompletion", undefined, false, undefined);
33-
target = settings.getEffectiveConfigurationTarget("helpCompletion");
30+
await changeSetting("helpCompletion", undefined, false, undefined);
31+
target = getEffectiveConfigurationTarget("helpCompletion");
3432
assert.strictEqual(target, undefined);
3533
});
3634
});

0 commit comments

Comments
 (0)