### Configure Mixed Parallel and Sequential Commands Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/README.md This configuration showcases a mix of asynchronous and sequential commands. The `isAsync: true` setting allows the next command to start before the current one finishes, enabling parallel execution where specified. ```jsonc { "emeraldwalk.runonsave": { "commands": [ { "match": ".*", // This tells next command to run immediately after // this one starts instead of waiting for it to complete "isAsync": true, "cmd": "echo 'I run for all files.'", }, { "match": "\\.txt$", "cmd": "echo 'I am a .txt file ${file}'.", }, { "match": "\\.js$", "cmd": "echo 'I am a .js file ${file}'.", }, { "match": ".*", "cmd": "echo 'I am ${env.USERNAME}'.", }, ], }, } ``` -------------------------------- ### Sequential vs. Parallel Execution (`isAsync`) Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Controls whether commands execute sequentially (default) or asynchronously. Setting `isAsync` to `true` allows subsequent commands to start before the current one finishes. ```jsonc { "emeraldwalk.runonsave": { "commands": [ { // Runs first; next command waits until this exits "match": ".*\\.ts$", "isAsync": false, "cmd": "npx eslint ${file} --fix" }, { // Starts immediately after the previous command STARTS // (does not wait for eslint to finish) "match": ".*\\.ts$", "isAsync": true, "cmd": "npx prettier --write ${file}" }, { // Starts immediately after the previous command STARTS "match": ".*\\.ts$", "isAsync": true, "cmd": "npx tsc --noEmit" }, { // Sequential again: waits for all preceding parallel commands "match": ".*\\.ts$", "isAsync": false, "cmd": "echo 'All formatters launched.'" } ] } } ``` -------------------------------- ### Get Replacements for Placeholder Tokens Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Use this utility to generate replacement pairs for placeholder tokens in command strings. It takes a `vscode.Uri` as input and returns an array of `[RegExp, replacer]` tuples. ```typescript import { getReplacements, doReplacement } from './utils'; import * as vscode from 'vscode'; const uri = vscode.Uri.file('/projects/app/src/components/Button.tsx'); const replacements = getReplacements(uri); // Apply to a command template const cmd = 'prettier --write ${file} # ext: ${fileExtname}, base: ${fileBasenameNoExt}'; const resolved = doReplacement(cmd, replacements); // → 'prettier --write /projects/app/src/components/Button.tsx # ext: .tsx, base: Button' // Apply to a message template with an env variable const msg = 'Saved ${relativeFile} as ${env.USER}'; const resolvedMsg = doReplacement(msg, replacements); // → 'Saved src/components/Button.tsx as alice' // doReplacement safely handles null/undefined input console.log(doReplacement(null, replacements)); // null console.log(doReplacement('', replacements)); // '' ``` -------------------------------- ### Configure Global Settings for Run On Save Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Set global options like the shell, console clearing, and messages in your VS Code `settings.json`. This example shows how to define a basic command to run TypeScript compilation on save. ```jsonc // .vscode/settings.json { "emeraldwalk.runonsave": { // Optional: custom shell (passed to child_process.exec) "shell": "/bin/bash", // Optional: clear the output panel before each save cycle (default: false) "autoClearConsole": true, // Optional: skip command execution when a file is saved without changes (default: false) "ignoreUnchangedFiles": true, // Optional: message printed before any commands run "message": "▶ Running save commands...", // Optional: message printed after all commands finish "messageAfter": "✔ Done.", // Optional: print total elapsed time after all commands "showElapsed": true, "commands": [ { "match": ".*\.ts$", "cmd": "npx tsc --noEmit" } ] } } ``` -------------------------------- ### Get Workspace Folder Path Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Resolves the workspace folder root for a given URI. If the file is not part of an open workspace, it defaults to the file's parent directory. This is used as the current working directory for `child_process.exec` calls. ```typescript import { getWorkspaceFolderPath } from './utils'; import * as vscode from 'vscode'; // File inside an open workspace const uri1 = vscode.Uri.file('/projects/app/src/index.ts'); // vscode.workspace.getWorkspaceFolder returns { uri: { fsPath: '/projects/app' } } console.log(getWorkspaceFolderPath(uri1)); // → '/projects/app' // File outside any workspace (e.g., opened as a standalone file) const uri2 = vscode.Uri.file('/tmp/scratch.ts'); // vscode.workspace.getWorkspaceFolder returns undefined console.log(getWorkspaceFolderPath(uri2)); // → '/tmp' (parent directory of the file) ``` -------------------------------- ### Activate Extension and Handle Configuration/Save Events Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt This code initializes the `ExtensionController` and sets up event listeners for configuration changes and file save operations. It also includes runtime enable/disable toggles and manual command triggering. ```typescript import { ExtensionController } from './ExtensionController'; import * as vscode from 'vscode'; // Activated by VS Code via extension.ts export function activate(context: vscode.ExtensionContext): void { const extension = new ExtensionController(context); // Print enabled/disabled status to the output channel on startup extension.showOutputMessage(); // Reload config whenever the user changes settings.json vscode.workspace.onDidChangeConfiguration(() => { const status = extension.showStatusMessage('Run On Save: Reloading config.'); extension.loadConfig(); status.dispose(); // clears the status bar message }); // Wire save events vscode.workspace.onWillSaveTextDocument((e) => extension.onWillSave(e.document)); vscode.workspace.onDidSaveTextDocument((doc) => extension.onDidSave(doc)); // Notebook support vscode.workspace.onWillSaveNotebookDocument((e) => extension.onWillSave(e.notebook)); vscode.workspace.onDidSaveNotebookDocument((nb) => extension.onDidSave(nb)); } // Runtime enable/disable (persisted in globalState across sessions) extension.isEnabled = false; // disable extension.isEnabled = true; // re-enable console.log(extension.isEnabled); // true // Manually trigger commands for a document (e.g., in tests) await extension.runCommands(document); // Read current config values console.log(extension.shell); // e.g. "/bin/bash" console.log(extension.autoClearConsole); // true/false console.log(extension.ignoreUnchangedFiles); // true/false console.log(extension.commands); // Array ``` -------------------------------- ### Configure Commands for All Files Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/README.md Use this configuration to run a command whenever any file is saved. The `match` regex `.*` ensures all files are included. ```jsonc { "emeraldwalk.runonsave": { "commands": [ { // Run whenever any file is saved "match": ".*", "cmd": "echo '${fileBasename}' saved.", }, ], }, } ``` -------------------------------- ### Placeholder Tokens in Run On Save Commands Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Demonstrates the use of various placeholder tokens that are substituted at runtime with file-specific information. These tokens can be used in `cmd`, `message`, and `messageAfter` fields. ```jsonc // .vscode/settings.json — demonstrating all available tokens { "emeraldwalk.runonsave": { "commands": [ { "match": ".*", "cmd": "echo file=${file} base=${fileBasename} dir=${fileDirname} ext=${fileExtname} noext=${fileBasenameNoExt} rel=${relativeFile} ws=${workspaceFolder} cwd=${cwd} user=${env.USER}", // For a file saved at /projects/app/src/components/Button.tsx in // workspace /projects/app, the command expands to: // // echo file=/projects/app/src/components/Button.tsx \ // base=Button.tsx \ // dir=/projects/app/src/components \ // ext=.tsx \ // noext=Button \ // rel=src/components/Button.tsx \ // ws=/projects/app \ // cwd=/usr/local/bin \ // user=alice } ] } } ``` -------------------------------- ### Configure Run On Save Settings Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/README.md Configure messages, elapsed time display, and commands to execute on file save. Use 'match' to specify which files trigger commands. ```jsonc { "emeraldwalk.runonsave": { // Messages to show before & after all commands "message": "*** All Start ***", "messageAfter": "*** All Complete ***", // Show elapsed time for all commands "showElapsed": true, "commands": [ { "match": ".*", "cmd": "echo 1st Command", // Messages to run before / after this cmd "message": "- 1. Start", "messageAfter": "- 1. Complete", // Show elapsed time for this cmd "showElapsed": true, }, { "message": "Message only", }, { "match": ".*", "cmd": "echo 2nd Command", // Messages to run before / after this cmd "message": "- 2. Start", "messageAfter": "- 2. Complete", // Show elapsed time for this cmd "showElapsed": true, }, ], }, } ``` -------------------------------- ### Define Detailed Command Configurations Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Configure individual commands within the `commands` array in `settings.json`. This includes precise file matching, asynchronous execution, custom messages, and output panel visibility. ```jsonc // .vscode/settings.json — full ICommand field reference { "emeraldwalk.runonsave": { "commands": [ { // Regex tested against the absolute file path. // Omit or set to "" to match all files. "match": "\\.(html|css|ts)$", // Regex for files that should NOT trigger this command. // notMatch wins over match. "notMatch": "node_modules/.*", // Shell command to execute. Supports placeholder tokens (see below). "cmd": "echo 'Saved: ${fileBasename}'", // If true, the next command starts before this one finishes (parallel). // Default: false (sequential). "isAsync": false, // Message printed to output panel before this command runs. "message": "→ Linting...", // Message printed after this command finishes. "messageAfter": "← Lint complete.", // Print elapsed ms for this command. "showElapsed": true, // Controls output panel visibility: // "never" - never change panel visibility (default) // "always" - show panel as soon as command starts // "error" - show panel only when command exits non-zero "autoShowOutputPanel": "error" } ] } } ``` -------------------------------- ### Internal Flow of ExtensionController.runCommands Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Illustrates the programmatic steps within `runCommands`, including checking enabled state, filtering commands by URI match/notMatch regex, resolving placeholder tokens, and executing commands sequentially or asynchronously. ```typescript // Internal flow — illustrating what runCommands does programmatically: // 1. Check enabled state and non-empty commands list if (!extension.isEnabled || extension.commands.length === 0) return; // 2. Filter by match/notMatch regex against document.uri.fsPath const matchingCommands = extension.commands.filter((cfg) => { const isMatch = !cfg.match || new RegExp(cfg.match).test(document.uri.fsPath); const isNegate = cfg.notMatch && new RegExp(cfg.notMatch).test(document.uri.fsPath); return isMatch && !isNegate; }); // 3. Resolve placeholder tokens (${file}, ${fileBasename}, ${env.X}, etc.) const resolved = matchingCommands.map((cfg) => ({ ...cfg, cmd: doReplacement(cfg.cmd, getReplacements(document.uri)), message: doReplacement(cfg.message, getReplacements(document.uri)), })); // 4. Execute — sequential commands await; isAsync commands fire-and-forget await extension.runCommands(document); // Output in "Run On Save" channel: // → Saving Button.tsx // npx eslint /projects/app/src/components/Button.tsx --fix // Elapsed ms: 342 // ← Lint complete. ``` -------------------------------- ### Run Unit Tests with Vitest Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/CONTRIBUTING.md Execute unit tests using Vitest. Ensure all tests pass before committing. ```sh npm test ``` -------------------------------- ### Configure Commands for Specific File Extensions Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/README.md This configuration runs commands only when files with specified extensions (e.g., html, css, js) are saved. The `match` regex targets these extensions. ```jsonc { "emeraldwalk.runonsave": { "commands": [ { // Run whenever html, css, or js files are saved "match": "\\.(html|css|js)$", "cmd": "echo '${fileBasename}' saved.", }, ], }, } ``` -------------------------------- ### Perform Pre-Release Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/CONTRIBUTING.md Script to perform a pre-release. Update the 'patch' argument to the next patch version. ```sh # Update the arg to the next patch version scripts/pre-release.sh patch ``` -------------------------------- ### File Matching with `match` and `notMatch` Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Configures commands to run only on files matching specific regex patterns, with `notMatch` taking precedence. This allows for granular control over which files trigger commands. ```jsonc { "emeraldwalk.runonsave": { "commands": [ { // Match only TypeScript source files, excluding test files and node_modules "match": "\\.ts$", "notMatch": "(\\.spec\\.ts$|\\.test\\.ts$|node_modules/.*)", "cmd": "npx tsc --noEmit --project tsconfig.json" }, { // Match all JSON files except those inside .vscode/ "match": "\\.json$", "notMatch": "\\.vscode/.*\\.json$", "cmd": "npx prettier --write ${file}" }, { // On Windows: target files inside a specific folder // Backslashes need double-escaping: once for regex, once for JSON "match": "src\\\\components\\\\.*\\.tsx$", "cmd": "npx stylelint ${file}" } ] } } ``` -------------------------------- ### Configure Commands Excluding Specific Folders Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/README.md This configuration demonstrates how to match files with a specific extension while excluding files within a particular directory (e.g., the .vscode folder). The `notMatch` regex prevents commands from running on files in the specified path. ```jsonc { "emeraldwalk.runonsave": { "commands": [ { // Match all .json files except for ones in // .vscode directory "match": "\\.json$", "notMatch": "\\.vscode/.*$", "cmd": "echo '${fileBasename}' saved.", }, ], }, } ``` -------------------------------- ### Configuration: Run Tests for Saved File Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt This JSON configuration sets up the Run On Save extension to execute tests relevant to the file that was just saved. It supports running specific tests for spec files and related tests for other TypeScript files. ```jsonc { "emeraldwalk.runonsave": { "commands": [ { "match": "\\.spec\\.ts$", "cmd": "npx vitest run ${file}", "message": "Running tests for ${fileBasename}...", "messageAfter": "Tests done.", "autoShowOutputPanel": "error" }, { "match": "\\.ts$", "notMatch": "\\.spec\\.ts$", "cmd": "npx vitest related ${file} --run", "message": "Running related tests...", "autoShowOutputPanel": "error" } ] } } ``` -------------------------------- ### Configure Commands Excluding Specific Files Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/README.md This configuration runs commands on a set of files but excludes specific files using the `notMatch` option. Ensure backslashes are double-escaped in JSON strings for regex patterns. ```jsonc { "emeraldwalk.runonsave": { "commands": [ { // Match all html, css, and js files // except for `exclude-me.js` "match": "\\.(html|css|js)$", "notMatch": "exclude-me\\.js$", "cmd": "echo '${fileBasename}' saved.", }, ], }, } ``` -------------------------------- ### Publish Release Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/CONTRIBUTING.md Script to publish a release. Adjust the tag version to the corresponding release version (e.g., v1.1.2-pre -> 1.0.2). ```sh ./scripts/publish.sh 1.0.x ``` -------------------------------- ### Configuration: Lint, Format, and Type-Check TypeScript on Save Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt This JSON configuration enables a pipeline to automatically lint, format, and type-check TypeScript files upon saving. It includes options for asynchronous execution and error reporting. ```jsonc { "emeraldwalk.runonsave": { "autoClearConsole": true, "ignoreUnchangedFiles": true, "message": "▶ On Save pipeline starting...", "messageAfter": "✔ Pipeline complete.", "showElapsed": true, "commands": [ { "match": "\\.tsx?$", "notMatch": "(node_modules|dist)/.*", "isAsync": false, "cmd": "npx eslint --fix ${file}", "message": "ESLint...", "autoShowOutputPanel": "error" }, { "match": "\\.tsx?$", "notMatch": "(node_modules|dist)/.*", "isAsync": true, "cmd": "npx prettier --write ${file}", "message": "Prettier..." }, { "match": "\\.tsx?$", "notMatch": "(node_modules|dist)/.*", "isAsync": true, "cmd": "npx tsc --noEmit", "message": "TypeScript check...", "autoShowOutputPanel": "error" } ] } } ``` -------------------------------- ### SaveTracker Class Usage Scenarios Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Demonstrates how `SaveTracker` distinguishes between content-change saves and no-op saves to implement the `ignoreUnchangedFiles` setting. It shows scenarios for dirty files, clean files, and multiple rapid saves. ```typescript import { SaveTracker } from './SaveTracker'; const runner = (document: Document) => { console.log(`Running commands for ${document.uri.fsPath}`); }; const tracker = new SaveTracker( runner, () => true, // ignoreUnchangedFiles = true ); // Scenario 1: dirty file saved — commands run const dirtyDoc = { uri: { fsPath: '/app/src/index.ts' }, isDirty: true }; tracker.onWillSave(dirtyDoc); console.log(tracker.getPendingSaveCount(dirtyDoc.uri)); // 1 tracker.onDidSave(dirtyDoc); // runner IS called — file had changes console.log(tracker.getPendingSaveCount(dirtyDoc.uri)); // 0 // Scenario 2: clean file saved (no changes) with ignoreUnchangedFiles=true — commands skipped const cleanDoc = { uri: { fsPath: '/app/src/index.ts' }, isDirty: false }; tracker.onWillSave(cleanDoc); console.log(tracker.getPendingSaveCount(cleanDoc.uri)); // 0 — not enqueued (not dirty) tracker.onDidSave(cleanDoc); // runner is NOT called — file was unchanged and ignoreUnchangedFiles is true // Scenario 3: multiple rapid saves — pending count queues up tracker.onWillSave(dirtyDoc); // count → 1 tracker.onWillSave(dirtyDoc); // count → 2 tracker.onDidSave(dirtyDoc); // count → 1, runner called tracker.onDidSave(dirtyDoc); // count → 0, runner called ``` -------------------------------- ### Verify Publisher Authentication Source: https://github.com/emeraldwalk/vscode-runonsave/blob/main/CONTRIBUTING.md Check if the VSCE authentication token is still valid before publishing. Use your Azure DevOps PAT if prompted. ```sh npx vsce login ``` -------------------------------- ### Binding Run On Save Toggle to Keyboard Shortcut Source: https://context7.com/emeraldwalk/vscode-runonsave/llms.txt Configures a keyboard shortcut in VS Code's `keybindings.json` to toggle the Run On Save extension's enabled state. ```jsonc // .vscode/keybindings.json — bind Toggle to a keyboard shortcut [ { "key": "ctrl+shift+s", "command": "extension.emeraldwalk.toggleRunOnSave" } ] ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.