-
Couldn't load subscription status.
- Fork 19
Description
How to Update Node.js Version and Modernize Tooling in a VS Code Extension Workspace
This guide walks you through updating the Node.js version and modernizing dependencies and configuration for a VS Code extension project. These steps are based on recent changes made in the vscode-black-formatter extension.
1. Update Node.js Version
-
CI/CD Pipelines:
-
Update Node.js version in your CI/CD config files (e.g., GitHub Actions, Azure Pipelines, etc.)
- Example for GitHub Actions:
env: NODE_VERSION: 22.17.0 jobs: build: steps: - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }}
- Example for Azure Pipelines:
- task: NodeTool@0 inputs: versionSpec: '22.17.0'
- Example for GitHub Actions:
-
Replace Emoji-Based Working Directory Names:
- If you see config like:
Replace
special-working-directory: './🐍 🐛' special-working-directory-relative: '🐍 🐛'
'🐍 🐛'with a simple name, e.g.:special-working-directory: './testingDir' special-working-directory-relative: 'testingDir'
- This avoids cross-platform issues and improves reliability in CI/CD and local testing.
- If you see config like:
-
-
Local Development:
- Update your local Node.js version using nvm or your preferred version manager:
nvm install 22.17.0 nvm use 22.17.0
- Update your local Node.js version using nvm or your preferred version manager:
2. Update Dependencies
- Open
package.jsonand update major dependencies:- TypeScript, ESLint, Mocha, Sinon, etc.
- Use
npm outdatedto see what can be updated. - Example:
"devDependencies": { "typescript": "^5.9.2", "eslint": "^9.33.0", "mocha": "^11.7.1", "sinon": "^21.0.0", // ...other dependencies }
- Run:
npm install
3. (Optional) Modernize ESLint Configuration
If you want to update to the latest ESLint configuration style:
- Remove old
.eslintrc.jsonor.eslintrc.js. - Create a new
eslint.config.mjsusing the latest ESLint API:// @ts-check import eslint from '@eslint/js'; import tseslint from 'typescript-eslint'; export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.recommended, { files: ['**/*.ts'], rules: { '@typescript-eslint/naming-convention': 'warn', 'curly': 'warn', 'eqeqeq': 'warn', 'no-throw-literal': 'warn', 'semi': 'off', }, }, { ignores: [ 'out/**', 'dist/**', '**/*.d.ts', ], } );
- Install new ESLint dependencies:
npm install @eslint/js typescript-eslint eslint --save-dev
4. Update Python Requirements (if applicable)
- If your extension uses Python, update
requirements.txtand test requirements to latest versions. - Use
pip-compileoruvto regenerate lock files.
5. Refactor Test and Build Scripts
- Update test scripts to use new Node.js and dependency APIs.
- Add verbose logging and error handling for extension activation and installation.
- Ensure working directories are compatible across platforms (avoid emojis, use simple names like
testingDir).
Important: Fixing spawnSync Usage for Node.js 22+
Node.js 22 introduced stricter requirements for the arguments passed to spawnSync. If you use code like:
const command = path.relative(EXTENSION_ROOT_DIR, cli);
cp.spawnSync(command, [...args, '--install-extension', 'ms-python.python'], { ... });You may encounter errors about invalid arguments or command not found. To fix this:
- Use the full path to the CLI executable (do not use
path.relative). - Pass arguments as a flat array, and set
shell: trueonly on Windows. - Add verbose logging for debugging.
Example: Robust Extension Installation for Node.js 22+
async function main() {
try {
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../');
// The path to test runner
// Passed to --extensionTestsPath
const extensionTestsPath = path.resolve(__dirname, './unittest/index');
const vscodeExecutablePath = await downloadAndUnzipVSCode('stable');
const [cliPath, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath);
// Use cp.spawn / cp.exec for custom setup
const isWin = process.platform === 'win32';
if (isWin) {
try {
const installResult = cp.spawnSync(
cliPath,
[...args, '--install-extension', PVSC_EXTENSION_ID_FOR_TESTS, PVSC_ENVS_EXTENSION_ID_FOR_TESTS],
{
cwd: path.dirname(cliPath),
encoding: 'utf8',
stdio: 'inherit',
shell: true,
},
);
if (installResult.error) {
console.error('Extension installation error:', installResult.error);
}
if (installResult.status !== 0) {
console.error(`Extension installation failed with exit code: ${installResult.status}`);
} else {
console.log('Extension installation succeeded.');
}
} catch (ex) {
console.error('Exception during extension installation:', ex);
}
} else {
const installResult = cp.spawnSync(
cliPath,
[...args, '--install-extension', PVSC_EXTENSION_ID_FOR_TESTS, PVSC_ENVS_EXTENSION_ID_FOR_TESTS],
{
encoding: 'utf8',
stdio: 'inherit',
},
);
if (installResult.error) {
console.error('Extension installation error:', installResult.error);
}
if (installResult.status !== 0) {
console.error(`Extension installation failed with exit code: ${installResult.status}`);
} else {
console.log('Extension installation succeeded.');
}
}
console.log('Extensions installed, ready to run tests.');
// Run the extension test
await runTests({
// Use the specified `code` executable
vscodeExecutablePath,
extensionDevelopmentPath,
extensionTestsPath,
});
} catch (err) {
console.error('Failed to run tests');
process.exit(1);
}
}This pattern ensures compatibility with Node.js 22+ and avoids common issues with extension installation in smoke tests. Always check for errors and exit codes, and use platform-specific options for Windows.
6. Clean Up and Validate
- Remove unused imports and obsolete configuration.
- Run all tests to ensure compatibility.
- Commit and push your changes.
other notes:
update the checkout actions to v4 as well
add the github label "debt"
Tip: Review recent PRs in similar extensions for examples of dependency and config updates.