Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,30 @@
"focus": true,
"panel": "dedicated"
}
},
{
"label": "reset-dev",
"detail": "Recovery: kill stale tsc/vite/watch-all watchers and Extension Development Host windows for this extension when a launch gets wedged",
"type": "shell",
"command": "pkill -f 'vscode-brightscript-language.*[w]atch-all\\.ts'; pkill -f 'vscode-brightscript-language.*[t]sc -w'; pkill -f 'vscode-brightscript-language.*[v]ite build --watch'; pkill -f 'extensionDevelopmentPath=[^ ]*vscode-brightscript-language'; echo 'dev environment reset'",
"windows": {
"command": "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -and ($_.CommandLine -match 'vscode-brightscript-language.*([w]atch-all\\.ts|[t]sc -w|[v]ite build --watch)' -or $_.CommandLine -match 'extensionDevelopmentPath=[^ ]*vscode-brightscript-language') } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }; Write-Host 'dev environment reset'",
"options": {
"shell": {
"executable": "powershell.exe",
"args": [
"-NoProfile",
"-Command"
]
}
}
},
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "shared",
"clear": true
}
}
]
}
2 changes: 1 addition & 1 deletion scripts/releases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import * as yargs from 'yargs';
import * as fsExtra from 'fs-extra';
import { standardizePath as s } from 'brighterscript';
import { execSync, exec } from 'child_process';
import { execSync } from 'child_process';
import * as chalk from 'chalk';
import * as semver from 'semver';
import * as prompt from 'prompt';
Expand Down
11 changes: 11 additions & 0 deletions scripts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDir": ".",
"target": "es2020",
"noEmit": true
},
"include": [
"**/*"
]
}
76 changes: 72 additions & 4 deletions scripts/watch-all.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as fsExtra from 'fs-extra';
import * as path from 'path';
import { spawn } from 'child_process';
import * as os from 'os';
import * as crypto from 'crypto';
import { spawn, spawnSync } from 'child_process';
import type { ChildProcess } from 'child_process';
import * as debounce from 'debounce';
import * as chalk from 'chalk';
import * as dayjs from 'dayjs';
Expand Down Expand Up @@ -32,7 +35,68 @@ class Logger {

const logger = new Logger();

logger.writeLine(`${timestamp()} Starting compilation in watch mode...`);
const repoRoot = path.resolve(__dirname, '..');
//scope the pid file to this checkout so parallel clones don't kill each other's watchers
const pidFile = path.join(
os.tmpdir(),
`vscode-brightscript-watch-all-${crypto.createHash('md5').update(repoRoot).digest('hex').slice(0, 8)}.json`
);
const watchers: ChildProcess[] = [];

function killGroup(pid: number) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's harden this slightly, to ensure we don't just kill any arbitrary process with that id. (maybe store PID and process name) and compare them before doing the kill.

try {
if (process.platform === 'win32') {
spawnSync('taskkill', ['/pid', `${pid}`, '/t', '/f'], { stdio: 'ignore' });
} else {
//negative pid targets the whole process group (npm + tsc/vite),
//which a plain kill(pid) would leave running
process.kill(-pid, 'SIGKILL');
}
} catch {
//group is already gone - nothing to do
}
}

//kill any watchers left behind by a previous run that didn't shut down cleanly
//(VS Code killing the task, a crash, etc). This is what stops watcher processes
//from piling up across launches until the machine has to be restarted.
function cleanupPreviousRun() {
let previousPids: number[] = [];
try {
previousPids = fsExtra.readJsonSync(pidFile);
} catch {
return;
}
for (const pid of previousPids) {
killGroup(pid);
}
fsExtra.removeSync(pidFile);
}

function writePidFile() {
fsExtra.writeJsonSync(pidFile, watchers.map(watcher => watcher.pid).filter(pid => pid !== undefined));
}

function shutdown() {
for (const watcher of watchers) {
if (watcher.pid !== undefined) {
killGroup(watcher.pid);
}
}
fsExtra.removeSync(pidFile);
}

process.on('exit', shutdown);
for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const) {
process.on(signal, () => {
shutdown();
process.exit();
});
}

cleanupPreviousRun();

logger.writeLine(`[${timestamp()}] Starting compilation in watch mode...`);

//run watch tasks for every related project, in a single output window so we don't have 7 console tabs open
const projects = [{
Expand Down Expand Up @@ -122,7 +186,6 @@ function processData(project: Project, source: 'stdout' | 'stderr', data: string
const printStatus = debounce(() => {
let errorCount = 0;
let pendingCount = 0;
const diagnostics = [];
let status = projects.map(project => {
if (project.state === 'success') {
return chalk.green(`✔ ${project.name}`);
Expand Down Expand Up @@ -175,8 +238,13 @@ projects.forEach(async (project) => {
const watcher = spawn('npm', ['run', 'watch'], {
cwd: project.path,
env: { ...process.env },
shell: true
shell: true,
//run each watcher in its own process group so shutdown can kill the entire
//npm -> tsc/vite subtree instead of orphaning it
detached: true
});
watchers.push(watcher);
writePidFile();

watcher.stdout.on('data', (data) => {
processData(project, 'stdout', data.toString());
Expand Down
Loading