Merge branch 'main' into copilot/fix-tooltip-markdown-syntax

This commit is contained in:
Matt Bierner
2026-03-04 09:34:36 -08:00
committed by GitHub
874 changed files with 39786 additions and 13561 deletions
-2
View File
@@ -18,8 +18,6 @@
**/extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts
**/extensions/terminal-suggest/third_party/**
**/extensions/typescript-language-features/test-workspace/**
**/extensions/typescript-language-features/extension.webpack.config.js
**/extensions/typescript-language-features/extension-browser.webpack.config.js
**/extensions/typescript-language-features/package-manager/node-maintainer/**
**/extensions/vscode-api-tests/testWorkspace/**
**/extensions/vscode-api-tests/testWorkspace2/**
+3
View File
@@ -26,6 +26,7 @@ src/vs/base/browser/ui/tree/** @joaomoreno @benibenj
# Platform
src/vs/platform/auxiliaryWindow/** @bpasero
src/vs/platform/backup/** @bpasero
src/vs/platform/browserView/** @kycutler @jruales
src/vs/platform/dialogs/** @bpasero
src/vs/platform/editor/** @bpasero
src/vs/platform/environment/** @bpasero
@@ -65,6 +66,7 @@ src/vs/code/** @bpasero @deepak1556
src/vs/workbench/services/activity/** @bpasero
src/vs/workbench/services/authentication/** @TylerLeonhardt
src/vs/workbench/services/auxiliaryWindow/** @bpasero
src/vs/workbench/services/browserView/** @kycutler @jruales
src/vs/workbench/services/contextmenu/** @bpasero
src/vs/workbench/services/dialogs/** @alexr00 @bpasero
src/vs/workbench/services/editor/** @bpasero
@@ -97,6 +99,7 @@ src/vs/workbench/electron-browser/** @bpasero
# Workbench Contributions
src/vs/workbench/contrib/authentication/** @TylerLeonhardt
src/vs/workbench/contrib/browserView/** @kycutler @jruales
src/vs/workbench/contrib/files/** @bpasero
src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @roblourens
src/vs/workbench/contrib/localization/** @TylerLeonhardt
+4
View File
@@ -42,3 +42,7 @@ Your response should include:
- Interpretation and analysis of the results
- References to specific documentation files when applicable
- Additional context or insights from the telemetry data
# Troubleshooting
If the connection to the Kusto cluster is timing out consistently, stop and ask the user to check whether they are connected to Azure VPN.
+8 -5
View File
@@ -24,6 +24,7 @@ Visual Studio Code is built with a layered architecture using TypeScript, web AP
- `workbench/api/` - Extension host and VS Code API implementation
- `src/vs/code/` - Electron main process specific implementation
- `src/vs/server/` - Server specific implementation
- `src/vs/sessions/` - Agent sessions window, a dedicated workbench layer for agentic workflows (sits alongside `vs/workbench`, may import from it but not vice versa)
The core architecture follows these principles:
- **Layered architecture** - from `base`, `platform`, `editor`, to `workbench`
@@ -49,15 +50,16 @@ Each extension follows the standard VS Code extension structure with `package.js
## Validating TypeScript changes
MANDATORY: Always check the `VS Code - Build` watch task output via #runTasks/getTaskOutput for compilation errors before running ANY script or declaring work complete, then fix all compilation errors before moving forward.
MANDATORY: Always check for compilation errors before running any tests or validation scripts, or declaring work complete, then fix all compilation errors before moving forward.
- NEVER run tests if there are compilation errors
- NEVER use `npm run compile` to compile TypeScript files but call #runTasks/getTaskOutput instead
- NEVER use `npm run compile` to compile TypeScript files
### TypeScript compilation steps
- Monitor the `VS Code - Build` task outputs for real-time compilation errors as you make changes
- This task runs `Core - Build` and `Ext - Build` to incrementally compile VS Code TypeScript sources and built-in extensions
- Start the task if it's not already running in the background
- If the `#runTasks/getTaskOutput` tool is available, check the `VS Code - Build` watch task output for compilation errors. This task runs `Core - Build` and `Ext - Build` to incrementally compile VS Code TypeScript sources and built-in extensions. Start the task if it's not already running in the background.
- If the tool is not available (e.g. in CLI environments) and you only changed code under `src/`, run `npm run compile-check-ts-native` after making changes to type-check the main VS Code sources (it validates `./src/tsconfig.json`).
- If you changed built-in extensions under `extensions/` and the tool is not available, run the corresponding gulp task `gulp compile-extensions` instead so that TypeScript errors in extensions are also reported.
- For TypeScript changes in the `build` folder, you can simply run `npm run typecheck` in the `build` folder.
### TypeScript validation steps
- Use the run test tool if you need to run tests. If that tool is not available, then you can use `scripts/test.sh` (or `scripts\test.bat` on Windows) for unit tests (add `--grep <pattern>` to filter tests) or `scripts/test-integration.sh` (or `scripts\test-integration.bat` on Windows) for integration tests (integration tests end with .integrationTest.ts or are in /extensions/).
@@ -134,6 +136,7 @@ function f(x: number, y: string): void { }
- Prefer regex capture groups with names over numbered capture groups.
- If you create any temporary new files, scripts, or helper files for iteration, clean up these files by removing them at the end of the task
- Never duplicate imports. Always reuse existing imports if they are present.
- When removing an import, do not leave behind blank lines where the import was. Ensure the surrounding code remains compact.
- Do not use `any` or `unknown` as the type for variables, parameters, or return values unless absolutely necessary. If they need type annotations, they should have proper types or interfaces defined.
- When adding file watching, prefer correlated file watchers (via fileService.createWatcher) to shared ones.
- When adding tooltips to UI elements, prefer the use of IHoverService service.
+14
View File
@@ -8,3 +8,17 @@ updates:
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-name: "@vscode/component-explorer"
- dependency-name: "@vscode/component-explorer-cli"
- package-ecosystem: "npm"
directory: "/build/vite"
schedule:
interval: "daily"
allow:
- dependency-name: "@vscode/component-explorer"
- dependency-name: "@vscode/component-explorer-vite-plugin"
+42
View File
@@ -0,0 +1,42 @@
{
"version": 1,
"hooks": {
"sessionStart": [
{
"type": "command",
"bash": "if [ -f ~/.vscode-worktree-setup ]; then nohup bash -c 'npm ci && npm run compile' > /tmp/worktree-setup-$(date +%Y-%m-%d_%H-%M-%S).log 2>&1 & fi",
"powershell": "if (Test-Path \"$env:USERPROFILE\\.vscode-worktree-setup\") { $log = \"$env:TEMP\\worktree-setup-$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').log\"; $dir = $PWD.Path; Start-Job -ScriptBlock { param($d, $l) Set-Location $d; & { npm ci; if ($LASTEXITCODE -eq 0) { npm run compile } } *> $l } -ArgumentList $dir, $log | Out-Null }"
}
],
"sessionEnd": [
{
"type": "command",
"bash": ""
}
],
"agentStop": [
{
"type": "command",
"bash": ""
}
],
"userPromptSubmitted": [
{
"type": "command",
"bash": ""
}
],
"preToolUse": [
{
"type": "command",
"bash": ""
}
],
"postToolUse": [
{
"type": "command",
"bash": ""
}
]
}
}
+2 -2
View File
@@ -6,7 +6,7 @@ description: Kusto exploration and telemetry analysis instructions
When performing Kusto queries, telemetry analysis, or data exploration tasks for VS Code, consult the comprehensive Kusto instructions located at:
**[kusto-vscode-instructions.md](../../../vscode-internalbacklog/instructions/kusto/kusto-vscode-instructions.md)**
**[kusto-vscode-instructions.md](../../../vscode-tools/.github/skills/kusto-telemetry/kusto-vscode.instructions.md)**
These instructions contain valuable information about:
- Available Kusto clusters and databases for VS Code telemetry
@@ -16,4 +16,4 @@ These instructions contain valuable information about:
Reading these instructions before writing Kusto queries will help you write more accurate and efficient queries, avoid common pitfalls, and leverage existing knowledge about VS Code's telemetry infrastructure.
(Make sure to have the main branch of vscode-internalbacklog up to date in case there are problems).
(Make sure to have the main branch of vscode-tools up to date in case there are problems and the repository cloned from https://github.com/microsoft/vscode-tools).
+11 -7
View File
@@ -45,7 +45,7 @@ When proposing or implementing changes, follow these rules from the spec:
4. **New parts go in the right section** — Any new parts should be added to the horizontal branch alongside Chat Bar and Auxiliary Bar
5. **Preserve no-op methods** — Unsupported features (zen mode, centered layout, etc.) should remain as no-ops, not throw errors
6. **Handle pane composite lifecycle** — When hiding/showing parts, manage the associated pane composites
7. **Use agent session parts** — New part functionality goes in the agent session part classes (`SidebarPart`, `AuxiliaryBarPart`, `PanelPart`, `ChatBarPart`), not the standard workbench parts
7. **Use agent session parts** — New part functionality goes in the agent session part classes (`SidebarPart`, `AuxiliaryBarPart`, `PanelPart`, `ChatBarPart`, `ProjectBarPart`), not the standard workbench parts
8. **Use separate storage keys** — Agent session parts use their own storage keys (prefixed with `workbench.agentsession.` or `workbench.chatbar.`) to avoid conflicts with regular workbench state
9. **Use agent session menu IDs** — Actions should use `Menus.*` menu IDs (from `sessions/browser/menus.ts`), not shared `MenuId.*` constants
@@ -53,20 +53,24 @@ When proposing or implementing changes, follow these rules from the spec:
| File | Purpose |
|------|---------|
| `sessions/LAYOUT.md` | Authoritative specification |
| `sessions/LAYOUT.md` | Authoritative layout specification |
| `sessions/browser/workbench.ts` | Main layout implementation (`Workbench` class) |
| `sessions/browser/menus.ts` | Agent sessions menu IDs (`Menus` export) |
| `sessions/browser/layoutActions.ts` | Layout actions (toggle sidebar, panel, secondary sidebar) |
| `sessions/browser/paneCompositePartService.ts` | `AgenticPaneCompositePartService` |
| `sessions/browser/style.css` | Layout-specific styles |
| `sessions/browser/parts/` | Agent session part implementations |
| `sessions/browser/media/style.css` | Layout-specific styles |
| `sessions/browser/parts/parts.ts` | `AgenticParts` enum |
| `sessions/browser/parts/titlebarPart.ts` | Titlebar part, MainTitlebarPart, AuxiliaryTitlebarPart, TitleService |
| `sessions/browser/parts/sidebarPart.ts` | Sidebar part (with footer) |
| `sessions/browser/parts/sidebarPart.ts` | Sidebar part (with footer and macOS traffic light spacer) |
| `sessions/browser/parts/chatBarPart.ts` | Chat Bar part |
| `sessions/browser/widget/` | Agent sessions chat widget |
| `sessions/browser/parts/auxiliaryBarPart.ts` | Auxiliary Bar part (with run script dropdown) |
| `sessions/browser/parts/panelPart.ts` | Panel part |
| `sessions/browser/parts/projectBarPart.ts` | Project Bar part (folder entries, icon customization) |
| `sessions/contrib/configuration/browser/configuration.contribution.ts` | Sets `workbench.editor.useModal` to `'all'` for modal editor overlay |
| `sessions/contrib/sessions/browser/sessionsTitleBarWidget.ts` | Title bar widget and session picker |
| `sessions/contrib/chat/browser/runScriptAction.ts` | Run script contribution |
| `sessions/contrib/chat/browser/runScriptAction.ts` | Run script split button for titlebar |
| `sessions/contrib/accountMenu/browser/account.contribution.ts` | Account widget for sidebar footer |
| `sessions/electron-browser/parts/titlebarPart.ts` | Desktop (Electron) titlebar part |
## 5. Testing Changes
+112 -32
View File
@@ -66,21 +66,24 @@ Use the [queue command](./azure-pipeline.ts) to queue a validation build:
```bash
# Queue a build on the current branch
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts queue
node .github/skills/azure-pipelines/azure-pipeline.ts queue
# Queue with a specific source branch
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts queue --branch my-feature-branch
node .github/skills/azure-pipelines/azure-pipeline.ts queue --branch my-feature-branch
# Queue with custom variables (e.g., to skip certain stages)
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts queue --variables "SKIP_TESTS=true"
# Queue with custom parameters
node .github/skills/azure-pipelines/azure-pipeline.ts queue --parameter "VSCODE_BUILD_WEB=false" --parameter "VSCODE_PUBLISH=false"
# Parameter value with spaces
node .github/skills/azure-pipelines/azure-pipeline.ts queue --parameter "VSCODE_BUILD_TYPE=Product Build"
```
> **Important**: Before queueing a new build, cancel any previous builds on the same branch that you no longer need. This frees up build agents and reduces resource waste:
> ```bash
> # Find the build ID from status, then cancel it
> node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status
> node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id <id>
> node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts queue
> node .github/skills/azure-pipelines/azure-pipeline.ts status
> node .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id <id>
> node .github/skills/azure-pipelines/azure-pipeline.ts queue
> ```
### Script Options
@@ -89,9 +92,43 @@ node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts
|--------|-------------|
| `--branch <name>` | Source branch to build (default: current git branch) |
| `--definition <id>` | Pipeline definition ID (default: 111) |
| `--variables <vars>` | Pipeline variables in `KEY=value` format, space-separated |
| `--parameter <entry>` | Pipeline parameter in `KEY=value` format (repeatable) |
| `--parameters <list>` | Space-separated parameters in `KEY=value KEY2=value2` format |
| `--dry-run` | Print the command without executing |
### Product Build Queue Parameters (`build/azure-pipelines/product-build.yml`)
| Name | Type | Default | Allowed Values | Description |
|------|------|---------|----------------|-------------|
| `VSCODE_QUALITY` | string | `insider` | `exploration`, `insider`, `stable` | Build quality channel |
| `VSCODE_BUILD_TYPE` | string | `Product Build` | `Product`, `CI` | Build mode for Product vs CI |
| `NPM_REGISTRY` | string | `https://pkgs.dev.azure.com/monacotools/Monaco/_packaging/vscode/npm/registry/` | any URL | Custom npm registry |
| `CARGO_REGISTRY` | string | `sparse+https://pkgs.dev.azure.com/monacotools/Monaco/_packaging/vscode/Cargo/index/` | any URL | Custom Cargo registry |
| `VSCODE_BUILD_WIN32` | boolean | `true` | `true`, `false` | Build Windows x64 |
| `VSCODE_BUILD_WIN32_ARM64` | boolean | `true` | `true`, `false` | Build Windows arm64 |
| `VSCODE_BUILD_LINUX` | boolean | `true` | `true`, `false` | Build Linux x64 |
| `VSCODE_BUILD_LINUX_SNAP` | boolean | `true` | `true`, `false` | Build Linux x64 Snap |
| `VSCODE_BUILD_LINUX_ARM64` | boolean | `true` | `true`, `false` | Build Linux arm64 |
| `VSCODE_BUILD_LINUX_ARMHF` | boolean | `true` | `true`, `false` | Build Linux armhf |
| `VSCODE_BUILD_ALPINE` | boolean | `true` | `true`, `false` | Build Alpine x64 |
| `VSCODE_BUILD_ALPINE_ARM64` | boolean | `true` | `true`, `false` | Build Alpine arm64 |
| `VSCODE_BUILD_MACOS` | boolean | `true` | `true`, `false` | Build macOS x64 |
| `VSCODE_BUILD_MACOS_ARM64` | boolean | `true` | `true`, `false` | Build macOS arm64 |
| `VSCODE_BUILD_MACOS_UNIVERSAL` | boolean | `true` | `true`, `false` | Build macOS universal (requires both macOS arches) |
| `VSCODE_BUILD_WEB` | boolean | `true` | `true`, `false` | Build Web artifacts |
| `VSCODE_PUBLISH` | boolean | `true` | `true`, `false` | Publish to builds.code.visualstudio.com |
| `VSCODE_RELEASE` | boolean | `false` | `true`, `false` | Trigger release flow if successful |
| `VSCODE_STEP_ON_IT` | boolean | `false` | `true`, `false` | Skip tests |
Example: run a quick CI-oriented validation with minimal publish/release side effects:
```bash
node .github/skills/azure-pipelines/azure-pipeline.ts queue \
--parameter "VSCODE_BUILD_TYPE=CI Build" \
--parameter "VSCODE_PUBLISH=false" \
--parameter "VSCODE_RELEASE=false"
```
---
## Checking Build Status
@@ -99,17 +136,17 @@ node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts
Use the [status command](./azure-pipeline.ts) to monitor a running build:
```bash
# Get status of the most recent build on your branch
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status
# Get status of the most recent builds
node .github/skills/azure-pipelines/azure-pipeline.ts status
# Get overview of a specific build by ID
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --build-id 123456
node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id 123456
# Watch build status (refreshes every 30 seconds)
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --watch
node .github/skills/azure-pipelines/azure-pipeline.ts status --watch
# Watch with custom interval (60 seconds)
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --watch 60
node .github/skills/azure-pipelines/azure-pipeline.ts status --watch 60
```
### Script Options
@@ -133,10 +170,10 @@ Use the [cancel command](./azure-pipeline.ts) to stop a running build:
```bash
# Cancel a build by ID (use status command to find IDs)
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id 123456
node .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id 123456
# Dry run (show what would be cancelled)
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id 123456 --dry-run
node .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id 123456 --dry-run
```
### Script Options
@@ -149,6 +186,44 @@ node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts
---
## Testing Pipeline Changes
When the user asks to **test changes in an Azure Pipelines build**, follow this workflow:
1. **Queue a new build** on the current branch
2. **Poll for completion** by periodically checking the build status until it finishes
### Polling for Build Completion
Use a shell loop with `sleep` to poll the build status. The `sleep` command works on all major operating systems:
```bash
# Queue the build and note the build ID from output (e.g., 123456)
node .github/skills/azure-pipelines/azure-pipeline.ts queue
# Poll every 60 seconds until complete (works on macOS, Linux, and Windows with Git Bash/WSL)
# Replace <BUILD_ID> with the actual build ID from the queue command
while true; do
node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id <BUILD_ID> --json 2>/dev/null | grep -q '"status": "completed"' && break
sleep 60
done
# Check final result
node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id <BUILD_ID>
```
Alternatively, use the built-in `--watch` flag which handles polling automatically:
```bash
node .github/skills/azure-pipelines/azure-pipeline.ts queue
# Use the build ID returned by the queue command
node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id <BUILD_ID> --watch
```
> **Note**: The `--watch` flag polls every 30 seconds by default. Use `--watch 60` for a 60-second interval to reduce API calls.
---
## Common Workflows
### 1. Quick Pipeline Validation
@@ -159,45 +234,50 @@ git add -A && git commit -m "test: pipeline changes"
git push origin HEAD
# Check for any previous builds on this branch and cancel if needed
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id <id> # if there's an active build
node .github/skills/azure-pipelines/azure-pipeline.ts status
node .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id <id> # if there's an active build
# Queue and watch the new build
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts queue
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --watch
node .github/skills/azure-pipelines/azure-pipeline.ts queue
node .github/skills/azure-pipelines/azure-pipeline.ts status --watch
```
### 2. Investigate a Build
```bash
# Get overview of a build (shows stages, artifacts, and log IDs)
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --build-id 123456
node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id 123456
# Download a specific log for deeper inspection
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --build-id 123456 --download-log 5
node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id 123456 --download-log 5
# Download an artifact
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --build-id 123456 --download-artifact unsigned_vscode_cli_win32_x64_cli
node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id 123456 --download-artifact unsigned_vscode_cli_win32_x64_cli
```
### 3. Test with Modified Variables
### 3. Test with Modified Parameters
```bash
# Skip expensive stages during validation
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts queue --variables "VSCODE_BUILD_SKIP_INTEGRATION_TESTS=true"
# Customize build matrix for quicker validation
node .github/skills/azure-pipelines/azure-pipeline.ts queue \
--parameter "VSCODE_BUILD_TYPE=CI Build" \
--parameter "VSCODE_BUILD_WEB=false" \
--parameter "VSCODE_BUILD_ALPINE=false" \
--parameter "VSCODE_BUILD_ALPINE_ARM64=false" \
--parameter "VSCODE_PUBLISH=false"
```
### 4. Cancel a Running Build
```bash
# First, find the build ID
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status
node .github/skills/azure-pipelines/azure-pipeline.ts status
# Cancel a specific build by ID
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id 123456
node .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id 123456
# Dry run to see what would be cancelled
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id 123456 --dry-run
node .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id 123456 --dry-run
```
### 5. Iterate on Pipeline Changes
@@ -210,12 +290,12 @@ git add -A && git commit --amend --no-edit
git push --force-with-lease origin HEAD
# Find the outdated build ID and cancel it
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id <id>
node .github/skills/azure-pipelines/azure-pipeline.ts status
node .github/skills/azure-pipelines/azure-pipeline.ts cancel --build-id <id>
# Queue a fresh build and monitor
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts queue
node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --watch
node .github/skills/azure-pipelines/azure-pipeline.ts queue
node .github/skills/azure-pipelines/azure-pipeline.ts status --watch
```
---
@@ -9,7 +9,7 @@
* A unified command-line tool for managing Azure Pipeline builds.
*
* Usage:
* node --experimental-strip-types azure-pipeline.ts <command> [options]
* node azure-pipeline.ts <command> [options]
*
* Commands:
* queue - Queue a new pipeline build
@@ -38,8 +38,8 @@ const NUMERIC_ID_PATTERN = /^\d+$/;
const MAX_ID_LENGTH = 15;
const BRANCH_PATTERN = /^[a-zA-Z0-9_\-./]+$/;
const MAX_BRANCH_LENGTH = 256;
const VARIABLE_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*=[a-zA-Z0-9_\-./: ]*$/;
const MAX_VARIABLE_LENGTH = 256;
const PARAMETER_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_]*=[a-zA-Z0-9_\-./: +]*$/;
const MAX_PARAMETER_LENGTH = 256;
const ARTIFACT_NAME_PATTERN = /^[a-zA-Z0-9_\-.]+$/;
const MAX_ARTIFACT_NAME_LENGTH = 256;
const MIN_WATCH_INTERVAL = 5;
@@ -88,7 +88,7 @@ interface Artifact {
interface QueueArgs {
branch: string;
definitionId: string;
variables: string;
parameters: string[];
dryRun: boolean;
help: boolean;
}
@@ -159,19 +159,18 @@ function validateBranch(value: string): void {
}
}
function validateVariables(value: string): void {
if (!value) {
function validateParameters(values: string[]): void {
if (!values.length) {
return;
}
const vars = value.split(' ').filter(v => v.length > 0);
for (const v of vars) {
if (v.length > MAX_VARIABLE_LENGTH) {
console.error(colors.red(`Error: Variable '${v.substring(0, 20)}...' is too long (max ${MAX_VARIABLE_LENGTH} characters)`));
for (const parameter of values) {
if (parameter.length > MAX_PARAMETER_LENGTH) {
console.error(colors.red(`Error: Parameter '${parameter.substring(0, 20)}...' is too long (max ${MAX_PARAMETER_LENGTH} characters)`));
process.exit(1);
}
if (!VARIABLE_PATTERN.test(v)) {
console.error(colors.red(`Error: Invalid variable format '${v}'`));
console.log('Expected format: KEY=value (alphanumeric, underscores, hyphens, dots, slashes, colons, spaces in value)');
if (!PARAMETER_PATTERN.test(parameter)) {
console.error(colors.red(`Error: Invalid parameter format '${parameter}'`));
console.log('Expected format: KEY=value (alphanumeric, underscores, hyphens, dots, slashes, colons, plus signs, spaces in value)');
process.exit(1);
}
}
@@ -612,7 +611,7 @@ class AzureDevOpsClient {
return JSON.parse(result);
}
async queueBuild(definitionId: string, branch: string, variables?: string): Promise<Build> {
async queueBuild(definitionId: string, branch: string, parameters: string[] = []): Promise<Build> {
const args = [
'pipelines', 'run',
'--organization', this.organization,
@@ -621,8 +620,8 @@ class AzureDevOpsClient {
'--branch', branch,
];
if (variables) {
args.push('--variables', ...variables.split(' '));
if (parameters.length > 0) {
args.push('--parameters', ...parameters);
}
args.push('--output', 'json');
@@ -771,7 +770,7 @@ class AzureDevOpsClient {
// ============================================================================
function printQueueUsage(): void {
const scriptName = 'node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts queue';
const scriptName = 'node .github/skills/azure-pipelines/azure-pipeline.ts queue';
console.log(`Usage: ${scriptName} [options]`);
console.log('');
console.log('Queue an Azure DevOps pipeline build for VS Code.');
@@ -779,21 +778,23 @@ function printQueueUsage(): void {
console.log('Options:');
console.log(' --branch <name> Source branch to build (default: current git branch)');
console.log(' --definition <id> Pipeline definition ID (default: 111)');
console.log(' --variables <vars> Pipeline variables in "KEY=value KEY2=value2" format');
console.log(' --parameter <entry> Pipeline parameter in "KEY=value" format (repeatable)');
console.log(' --parameters <list> Space-separated parameter list in "KEY=value KEY2=value2" format');
console.log(' --dry-run Print the command without executing');
console.log(' --help Show this help message');
console.log('');
console.log('Examples:');
console.log(` ${scriptName} # Queue build on current branch`);
console.log(` ${scriptName} --branch my-feature # Queue build on specific branch`);
console.log(` ${scriptName} --variables "SKIP_TESTS=true" # Queue with custom variables`);
console.log(` ${scriptName} --parameter "VSCODE_BUILD_WEB=false" --parameter "VSCODE_PUBLISH=false"`);
console.log(` ${scriptName} --parameter "VSCODE_BUILD_TYPE=Product Build" # Parameter values with spaces`);
}
function parseQueueArgs(args: string[]): QueueArgs {
const result: QueueArgs = {
branch: '',
definitionId: DEFAULT_DEFINITION_ID,
variables: '',
parameters: [],
dryRun: false,
help: false,
};
@@ -807,8 +808,15 @@ function parseQueueArgs(args: string[]): QueueArgs {
case '--definition':
result.definitionId = args[++i] || DEFAULT_DEFINITION_ID;
break;
case '--variables':
result.variables = args[++i] || '';
case '--parameter': {
const parameter = args[++i] || '';
if (parameter) {
result.parameters.push(parameter);
}
break;
}
case '--parameters':
result.parameters.push(...(args[++i] || '').split(' ').filter(v => v.length > 0));
break;
case '--dry-run':
result.dryRun = true;
@@ -829,7 +837,7 @@ function parseQueueArgs(args: string[]): QueueArgs {
function validateQueueArgs(args: QueueArgs): void {
validateNumericId(args.definitionId, '--definition');
validateBranch(args.branch);
validateVariables(args.variables);
validateParameters(args.parameters);
}
async function runQueueCommand(args: string[]): Promise<void> {
@@ -860,8 +868,8 @@ async function runQueueCommand(args: string[]): Promise<void> {
console.log(`Project: ${colors.green(PROJECT)}`);
console.log(`Definition: ${colors.green(parsedArgs.definitionId)}`);
console.log(`Branch: ${colors.green(branch)}`);
if (parsedArgs.variables) {
console.log(`Variables: ${colors.green(parsedArgs.variables)}`);
if (parsedArgs.parameters.length > 0) {
console.log(`Parameters: ${colors.green(parsedArgs.parameters.join(' '))}`);
}
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('');
@@ -875,8 +883,8 @@ async function runQueueCommand(args: string[]): Promise<void> {
'--id', parsedArgs.definitionId,
'--branch', branch,
];
if (parsedArgs.variables) {
cmdArgs.push('--variables', ...parsedArgs.variables.split(' '));
if (parsedArgs.parameters.length > 0) {
cmdArgs.push('--parameters', ...parsedArgs.parameters);
}
cmdArgs.push('--output', 'json');
console.log(`az ${cmdArgs.join(' ')}`);
@@ -887,7 +895,7 @@ async function runQueueCommand(args: string[]): Promise<void> {
try {
const client = new AzureDevOpsClient(ORGANIZATION, PROJECT);
const data = await client.queueBuild(parsedArgs.definitionId, branch, parsedArgs.variables);
const data = await client.queueBuild(parsedArgs.definitionId, branch, parsedArgs.parameters);
const buildId = data.id;
const buildNumber = data.buildNumber;
@@ -904,10 +912,10 @@ async function runQueueCommand(args: string[]): Promise<void> {
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('');
console.log('To check status, run:');
console.log(` node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --build-id ${buildId}`);
console.log(` node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id ${buildId}`);
console.log('');
console.log('To watch progress:');
console.log(` node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --build-id ${buildId} --watch`);
console.log(` node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id ${buildId} --watch`);
} catch (e) {
const error = e instanceof Error ? e : new Error(String(e));
console.error(colors.red('Error queuing build:'));
@@ -921,7 +929,7 @@ async function runQueueCommand(args: string[]): Promise<void> {
// ============================================================================
function printStatusUsage(): void {
const scriptName = 'node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status';
const scriptName = 'node .github/skills/azure-pipelines/azure-pipeline.ts status';
console.log(`Usage: ${scriptName} [options]`);
console.log('');
console.log('Get status and logs of an Azure DevOps pipeline build.');
@@ -1068,7 +1076,7 @@ async function runStatusCommand(args: string[]): Promise<void> {
if (!buildId) {
console.error(colors.red(`Error: No builds found for branch '${branch}'.`));
console.log('You can queue a new build with: node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts queue');
console.log('You can queue a new build with: node .github/skills/azure-pipelines/azure-pipeline.ts queue');
process.exit(1);
}
}
@@ -1162,7 +1170,7 @@ async function runStatusCommand(args: string[]): Promise<void> {
// ============================================================================
function printCancelUsage(): void {
const scriptName = 'node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts cancel';
const scriptName = 'node .github/skills/azure-pipelines/azure-pipeline.ts cancel';
console.log(`Usage: ${scriptName} --build-id <id> [options]`);
console.log('');
console.log('Cancel a running Azure DevOps pipeline build.');
@@ -1233,7 +1241,7 @@ async function runCancelCommand(args: string[]): Promise<void> {
console.error(colors.red('Error: --build-id is required.'));
console.log('');
console.log('To find build IDs, run:');
console.log(' node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status');
console.log(' node .github/skills/azure-pipelines/azure-pipeline.ts status');
process.exit(1);
}
@@ -1287,7 +1295,7 @@ async function runCancelCommand(args: string[]): Promise<void> {
console.log('');
console.log('The build will transition to "cancelling" state and then "canceled".');
console.log('Check status with:');
console.log(` node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts status --build-id ${buildId}`);
console.log(` node .github/skills/azure-pipelines/azure-pipeline.ts status --build-id ${buildId}`);
} catch (e) {
const error = e instanceof Error ? e : new Error(String(e));
console.error('');
@@ -1390,15 +1398,15 @@ async function runAllTests(): Promise<void> {
validateBranch('');
});
it('validateVariables accepts valid variable formats', () => {
validateVariables('KEY=value');
validateVariables('MY_VAR=some-value');
validateVariables('A=1 B=2 C=3');
validateVariables('PATH=/usr/bin:path');
it('validateParameters accepts valid parameter formats', () => {
validateParameters(['KEY=value']);
validateParameters(['MY_VAR=some-value']);
validateParameters(['A=1', 'B=2', 'C=3']);
validateParameters(['PATH=/usr/bin:path']);
});
it('validateVariables accepts empty string', () => {
validateVariables('');
it('validateParameters accepts empty list', () => {
validateParameters([]);
});
it('validateArtifactName accepts valid artifact names', () => {
@@ -1429,9 +1437,14 @@ async function runAllTests(): Promise<void> {
assert.strictEqual(args.definitionId, '222');
});
it('parseQueueArgs parses --variables correctly', () => {
const args = parseQueueArgs(['--variables', 'KEY=value']);
assert.strictEqual(args.variables, 'KEY=value');
it('parseQueueArgs parses --parameters correctly', () => {
const args = parseQueueArgs(['--parameters', 'KEY=value']);
assert.deepStrictEqual(args.parameters, ['KEY=value']);
});
it('parseQueueArgs parses repeated --parameter correctly', () => {
const args = parseQueueArgs(['--parameter', 'A=1', '--parameter', 'B=two words']);
assert.deepStrictEqual(args.parameters, ['A=1', 'B=two words']);
});
it('parseQueueArgs parses --dry-run correctly', () => {
@@ -1440,10 +1453,10 @@ async function runAllTests(): Promise<void> {
});
it('parseQueueArgs parses combined arguments', () => {
const args = parseQueueArgs(['--branch', 'main', '--definition', '333', '--variables', 'A=1 B=2', '--dry-run']);
const args = parseQueueArgs(['--branch', 'main', '--definition', '333', '--parameters', 'A=1 B=2', '--dry-run']);
assert.strictEqual(args.branch, 'main');
assert.strictEqual(args.definitionId, '333');
assert.strictEqual(args.variables, 'A=1 B=2');
assert.deepStrictEqual(args.parameters, ['A=1', 'B=2']);
assert.strictEqual(args.dryRun, true);
});
@@ -1516,12 +1529,12 @@ async function runAllTests(): Promise<void> {
assert.ok(cmd.includes('json'));
});
it('queueBuild includes variables when provided', async () => {
it('queueBuild includes parameters when provided', async () => {
const client = new TestableAzureDevOpsClient(ORGANIZATION, PROJECT);
await client.queueBuild('111', 'main', 'KEY=value OTHER=test');
await client.queueBuild('111', 'main', ['KEY=value', 'OTHER=test']);
const cmd = client.capturedCommands[0];
assert.ok(cmd.includes('--variables'));
assert.ok(cmd.includes('--parameters'));
assert.ok(cmd.includes('KEY=value'));
assert.ok(cmd.includes('OTHER=test'));
});
@@ -1718,7 +1731,7 @@ async function runAllTests(): Promise<void> {
describe('Integration Tests', () => {
it('full queue command flow constructs correct az commands', async () => {
const client = new TestableAzureDevOpsClient(ORGANIZATION, PROJECT);
await client.queueBuild('111', 'feature/test', 'DEBUG=true');
await client.queueBuild('111', 'feature/test', ['DEBUG=true']);
assert.strictEqual(client.capturedCommands.length, 1);
const cmd = client.capturedCommands[0];
@@ -1733,7 +1746,7 @@ async function runAllTests(): Promise<void> {
assert.ok(cmd.includes('111'));
assert.ok(cmd.includes('--branch'));
assert.ok(cmd.includes('feature/test'));
assert.ok(cmd.includes('--variables'));
assert.ok(cmd.includes('--parameters'));
assert.ok(cmd.includes('DEBUG=true'));
});
@@ -1797,7 +1810,7 @@ async function runAllTests(): Promise<void> {
// ============================================================================
function printMainUsage(): void {
const scriptName = 'node --experimental-strip-types .github/skills/azure-pipelines/azure-pipeline.ts';
const scriptName = 'node .github/skills/azure-pipelines/azure-pipeline.ts';
console.log(`Usage: ${scriptName} <command> [options]`);
console.log('');
console.log('Azure DevOps Pipeline CLI for VS Code builds.');
+343
View File
@@ -0,0 +1,343 @@
---
name: component-fixtures
description: Use when creating or updating component fixtures for screenshot testing, or when designing UI components to be fixture-friendly. Covers fixture file structure, theming, service setup, CSS scoping, async rendering, and common pitfalls.
---
# Component Fixtures
Component fixtures render isolated UI components for visual screenshot testing via the component explorer. Fixtures live in `src/vs/workbench/test/browser/componentFixtures/` and are auto-discovered by the Vite dev server using the glob `src/**/*.fixture.ts`.
Use tools `mcp_component-exp_`* to list and screenshot fixtures. If you cannot see these tools, inform the user to them on.
## Running Fixtures Locally
1. Start the component explorer daemon: run the **Launch Component Explorer** task
2. Use the `mcp_component-exp_list_fixtures` tool to see all available fixtures and their URLs
3. Use the `mcp_component-exp_screenshot` tool to capture screenshots programmatically
## File Structure
Each fixture file exports a default `defineThemedFixtureGroup(...)`. The file must end with `.fixture.ts`.
```
src/vs/workbench/test/browser/componentFixtures/
fixtureUtils.ts # Shared helpers (DO NOT import @vscode/component-explorer elsewhere)
myComponent.fixture.ts # Your fixture file
```
## Basic Pattern
```typescript
import { ComponentFixtureContext, createEditorServices, defineComponentFixture, defineThemedFixtureGroup } from './fixtureUtils.js';
export default defineThemedFixtureGroup({ path: 'myFeature/' }, {
Default: defineComponentFixture({ render: renderMyComponent }),
AnotherVariant: defineComponentFixture({ render: renderMyComponent }),
});
function renderMyComponent({ container, disposableStore, theme }: ComponentFixtureContext): void {
container.style.width = '400px';
const instantiationService = createEditorServices(disposableStore, {
colorTheme: theme,
additionalServices: (reg) => {
// Register additional services the component needs
reg.define(IMyService, MyServiceImpl);
reg.defineInstance(IMockService, mockInstance);
},
});
const widget = disposableStore.add(
instantiationService.createInstance(MyWidget, /* constructor args */)
);
container.appendChild(widget.domNode);
}
```
Key points:
- **`defineThemedFixtureGroup`** automatically creates Dark and Light variants for each fixture
- **`defineComponentFixture`** wraps your render function with theme setup and shadow DOM isolation
- **`createEditorServices`** provides a `TestInstantiationService` with base editor services pre-registered
- Always register created widgets with `disposableStore.add(...)` to prevent leaks
- Pass `colorTheme: theme` to `createEditorServices` so theme colors render correctly
## Utilities from fixtureUtils.ts
| Export | Purpose |
|---|---|
| `defineComponentFixture` | Creates Dark/Light themed fixture variants from a render function |
| `defineThemedFixtureGroup` | Groups multiple themed fixtures into a named fixture group |
| `createEditorServices` | Creates `TestInstantiationService` with all base editor services |
| `registerWorkbenchServices` | Registers additional workbench services (context menu, label, etc.) |
| `createTextModel` | Creates a text model via `ModelService` for editor fixtures |
| `setupTheme` | Applies theme CSS to a container (called automatically by `defineComponentFixture`) |
| `darkTheme` / `lightTheme` | Pre-loaded `ColorThemeData` instances |
**Important:** Only `fixtureUtils.ts` may import from `@vscode/component-explorer`. All fixture files must go through the helpers in `fixtureUtils.ts`.
## CSS Scoping
Fixtures render inside shadow DOM. The component-explorer automatically adopts the global VS Code stylesheets and theme CSS.
### Matching production CSS selectors
Many VS Code components have CSS rules scoped to deep ancestor selectors (e.g., `.interactive-session .interactive-input-part > .widget-container .my-element`). In fixtures, you must recreate the required ancestor DOM structure for these selectors to match:
```typescript
function render({ container }: ComponentFixtureContext): void {
container.classList.add('interactive-session');
// Recreate ancestor structure that CSS selectors expect
const inputPart = dom.$('.interactive-input-part');
const widgetContainer = dom.$('.widget-container');
inputPart.appendChild(widgetContainer);
container.appendChild(inputPart);
widgetContainer.appendChild(myWidget.domNode);
}
```
**Design recommendation for new components:** Avoid deeply nested CSS selectors that require specific ancestor elements. Use self-contained class names (e.g., `.my-widget .my-element` rather than `.parent-view .parent-part > .wrapper .my-element`). This makes components easier to fixture and reuse.
## Services
### Using createEditorServices
`createEditorServices` pre-registers these services: `IAccessibilityService`, `IKeybindingService`, `IClipboardService`, `IOpenerService`, `INotificationService`, `IDialogService`, `IUndoRedoService`, `ILanguageService`, `IConfigurationService`, `IStorageService`, `IThemeService`, `IModelService`, `ICodeEditorService`, `IContextKeyService`, `ICommandService`, `ITelemetryService`, `IHoverService`, `IUserInteractionService`, and more.
### Additional services
Register extra services via `additionalServices`:
```typescript
createEditorServices(disposableStore, {
additionalServices: (reg) => {
// Class-based (instantiated by DI):
reg.define(IMyService, MyServiceImpl);
// Instance-based (pre-constructed):
reg.defineInstance(IMyService, myMockInstance);
},
});
```
### Mocking services
Use the `mock<T>()` helper from `base/test/common/mock.js` to create mock service instances:
```typescript
import { mock } from '../../../../base/test/common/mock.js';
const myService = new class extends mock<IMyService>() {
override someMethod(): string { return 'test'; }
override onSomeEvent = Event.None;
};
reg.defineInstance(IMyService, myService);
```
For mock view models or data objects:
```typescript
const element = new class extends mock<IChatRequestViewModel>() { }();
```
## Async Rendering
The component explorer waits **2 animation frames** after the synchronous render function returns. For most components, this is sufficient.
If your render function returns a `Promise`, the component explorer waits for the promise to resolve.
### Pitfall: DOM reparenting causes flickering
Avoid moving rendered widgets between DOM parents after initial render. This causes:
- Layout recalculation (the widget jumps as `position: absolute` coordinates become invalid)
- Focus loss (blur events can trigger hide logic in widgets like QuickInput)
- Screenshot instability (the component explorer may capture an intermediate layout state)
**Bad pattern — reparenting a widget after async wait:**
```typescript
async function render({ container }: ComponentFixtureContext): Promise<void> {
const host = document.createElement('div');
container.appendChild(host);
// ... create widget inside host ...
await waitForWidget();
container.appendChild(widget); // BAD: reparenting causes flicker
host.remove();
}
```
**Better pattern — render in-place with the correct DOM structure from the start:**
```typescript
function render({ container }: ComponentFixtureContext): void {
// Set up the correct DOM structure first, then create the widget inside it
const widget = createWidget(container);
container.appendChild(widget.domNode);
}
```
If the component absolutely requires async setup (e.g., QuickInput which renders internally), minimize DOM manipulation after the widget appears by structuring the host container to match the final layout from the beginning.
## Adapting Existing Components for Fixtures
Existing components often need small changes to become fixturable. When writing a fixture reveals friction, fix the component — don't work around it in the fixture. Common adaptations:
### Decouple CSS from ancestor context
If a component's CSS only works inside a deeply nested selector like `.workbench .sidebar .my-view .my-widget`, refactor the CSS to be self-contained. Move the styles so they're scoped to the component's own root class:
```css
/* Before: requires specific ancestors */
.workbench .sidebar .my-view .my-widget .header { font-weight: bold; }
/* After: self-contained */
.my-widget .header { font-weight: bold; }
```
If the component shares styles with its parent (e.g., inheriting background color), use CSS custom properties rather than relying on ancestor selectors.
### Extract hard-coded service dependencies
If a component reaches into singletons or global state instead of using DI, refactor it to accept services through the constructor:
```typescript
// Before: hard to mock in fixtures
class MyWidget {
private readonly config = getSomeGlobalConfig();
}
// After: injectable and testable
class MyWidget {
constructor(@IConfigurationService private readonly configService: IConfigurationService) { }
}
```
### Add options to control auto-focus and animation
Components that auto-focus on creation or run animations cause flaky screenshots. Add an options parameter:
```typescript
interface IMyWidgetOptions {
shouldAutoFocus?: boolean;
}
```
The fixture passes `shouldAutoFocus: false`. The production call site keeps the default behavior.
### Expose internal state for "already completed" rendering
Many components have lifecycle states (loading → active → completed). If the component can only reach the "completed" state through user interaction, add support for initializing directly into that state via constructor data:
```typescript
// The fixture can pass pre-filled data to render the summary/completed state
// without simulating the full user interaction flow.
const carousel: IChatQuestionCarousel = {
questions,
allowSkip: true,
kind: 'questionCarousel',
isUsed: true, // Already completed
data: { 'q1': 'answer' }, // Pre-filled answers
};
```
### Make DOM node accessible
If a component builds its DOM internally and doesn't expose the root element, add a public `readonly domNode: HTMLElement` property so fixtures can append it to the container.
## Writing Fixture-Friendly Components
When designing new UI components, follow these practices to make them easy to fixture:
### 1. Accept a container element in the constructor
```typescript
// Good: container is passed in
class MyWidget {
constructor(container: HTMLElement, @IFoo foo: IFoo) {
this.domNode = dom.append(container, dom.$('.my-widget'));
}
}
// Also good: widget creates its own domNode for the caller to place
class MyWidget {
readonly domNode: HTMLElement;
constructor(@IFoo foo: IFoo) {
this.domNode = dom.$('.my-widget');
}
}
```
### 2. Use dependency injection for all services
All external dependencies should come through DI so fixtures can provide test implementations:
```typescript
// Good: services injected
constructor(@IThemeService private readonly themeService: IThemeService) { }
// Bad: reaching into globals
constructor() { this.theme = getGlobalTheme(); }
```
### 3. Keep CSS selectors shallow
```css
/* Good: self-contained, easy to fixture */
.my-widget .my-header { ... }
.my-widget .my-list-item { ... }
/* Bad: requires deep ancestor chain */
.workbench .sidebar .my-view .my-widget .my-header { ... }
```
### 4. Avoid reading from layout/window services during construction
Components that measure the window or read layout dimensions during construction are hard to fixture because the shadow DOM container has different dimensions than the workbench:
```typescript
// Prefer: use CSS for sizing, or accept dimensions as parameters
container.style.width = '400px';
container.style.height = '300px';
// Avoid: reading from layoutService during construction
const width = this.layoutService.mainContainerDimension.width;
```
### 5. Support disabling auto-focus in fixtures
Auto-focus can interfere with screenshot stability. Provide options to disable it:
```typescript
interface IMyWidgetOptions {
shouldAutoFocus?: boolean; // Fixtures pass false
}
```
### 6. Expose the DOM node
The fixture needs to append the widget's DOM to the container. Expose it as a public `readonly domNode: HTMLElement`.
## Multiple Fixture Variants
Create variants to show different states of the same component:
```typescript
export default defineThemedFixtureGroup({
// Different data states
Empty: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { items: [] }) }),
WithItems: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { items: sampleItems }) }),
// Different configurations
ReadOnly: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { readonly: true }) }),
Editable: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { readonly: false }) }),
// Lifecycle states
Loading: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { state: 'loading' }) }),
Completed: defineComponentFixture({ render: (ctx) => renderWidget(ctx, { state: 'done' }) }),
});
```
## Learnings
Update this section with insights from your fixture development experience!
* Do not copy the component to the fixture and modify it there. Always adapt the original component to be fixture-friendly, then render it in the fixture. This ensures the fixture tests the real component code and lifecycle, rather than a modified version that may hide bugs.
* **Don't recompose child widgets in fixtures.** Never manually instantiate and add a sub-widget (e.g., a toolbar content widget) that the parent component is supposed to create. Instead, configure the parent correctly (e.g., set the right editor option, register the right provider) so the child appears through the normal code path. Manually recomposing hides integration bugs and doesn't test the real widget lifecycle.
+70 -39
View File
@@ -15,8 +15,6 @@ The `src/vs/sessions/` directory contains authoritative specification documents.
| Layout spec | `src/vs/sessions/LAYOUT.md` | Grid structure, part positions, sizing, CSS classes, API reference |
| AI Customizations | `src/vs/sessions/AI_CUSTOMIZATIONS.md` | AI customization editor and tree view design |
| Chat Widget | `src/vs/sessions/browser/widget/AGENTS_CHAT_WIDGET.md` | Chat widget wrapper architecture, deferred session creation, option delivery |
| AI Customization Mgmt | `src/vs/sessions/contrib/aiCustomizationManagement/browser/SPEC.md` | Management editor specification |
| AI Customization Tree | `src/vs/sessions/contrib/aiCustomizationTreeView/browser/SPEC.md` | Tree view specification |
If you modify the implementation, you **must** update the corresponding spec to keep it in sync. Update the Revision History table at the bottom of `LAYOUT.md` with a dated entry.
@@ -62,44 +60,57 @@ src/vs/sessions/
├── AI_CUSTOMIZATIONS.md # AI customization design document
├── sessions.common.main.ts # Common (browser + desktop) entry point
├── sessions.desktop.main.ts # Desktop entry point (imports all contributions)
├── common/ # Shared types and context keys
── contextkeys.ts # ChatBar context keys
├── common/ # Shared types, context keys, and theme
── categories.ts # Command categories
│ ├── contextkeys.ts # ChatBar and welcome context keys
│ └── theme.ts # Theme contributions
├── browser/ # Core workbench implementation
│ ├── workbench.ts # Main Workbench class (implements IWorkbenchLayoutService)
│ ├── menus.ts # Agent sessions menu IDs (Menus export)
│ ├── layoutActions.ts # Layout toggle actions (sidebar, panel, auxiliary bar)
│ ├── paneCompositePartService.ts # AgenticPaneCompositePartService
│ ├── style.css # Layout-specific styles
│ ├── widget/ # Agent sessions chat widget
│ │ ── AGENTS_CHAT_WIDGET.md # Chat widget architecture doc
│ ├── agentSessionsChatWidget.ts # Main wrapper around ChatWidget
│ │ ├── agentSessionsChatTargetConfig.ts # Observable target state
│ │ ├── agentSessionsTargetPickerActionItem.ts # Target picker for input toolbar
│ │ ── media/
└── parts/ # Workbench part implementations
├── parts.ts # AgenticParts enum
├── titlebarPart.ts # Titlebar (3-section toolbar layout)
├── sidebarPart.ts # Sidebar (with footer for account widget)
── chatBarPart.ts # Chat Bar (primary chat surface)
├── auxiliaryBarPart.ts # Auxiliary Bar (with run script dropdown)
│ ├── panelPart.ts # Panel (terminal, output, etc.)
│ ├── projectBarPart.ts # Project bar (folder entries)
│ ├── agentSessionsChatInputPart.ts # Chat input part adapter
│ ├── agentSessionsChatWelcomePart.ts # Welcome view (mascot + target buttons + pickers)
│ └── media/ # Part CSS files
│ │ ── AGENTS_CHAT_WIDGET.md # Chat widget architecture doc
│ ├── parts/ # Workbench part implementations
│ │ ├── parts.ts # AgenticParts enum
│ │ ├── titlebarPart.ts # Titlebar (3-section toolbar layout)
│ │ ── sidebarPart.ts # Sidebar (with footer for account widget)
│ ├── chatBarPart.ts # Chat Bar (primary chat surface)
├── auxiliaryBarPart.ts # Auxiliary Bar
├── panelPart.ts # Panel (terminal, output, etc.)
├── projectBarPart.ts # Project bar (folder entries)
── media/ # Part CSS files
└── media/ # Layout-specific styles
├── electron-browser/ # Desktop-specific entry points
│ ├── sessions.main.ts # Desktop main bootstrap
│ ├── sessions.ts # Electron process entry
│ ├── sessions.html # Production HTML shell
── sessions-dev.html # Development HTML shell
── sessions-dev.html # Development HTML shell
│ ├── titleService.ts # Desktop title service override
│ └── parts/
│ └── titlebarPart.ts # Desktop titlebar part
├── services/ # Service overrides
│ ├── configuration/browser/ # Configuration service overrides
│ └── workspace/browser/ # Workspace service overrides
├── test/ # Unit tests
│ └── browser/
│ └── layoutActions.test.ts
└── contrib/ # Feature contributions
├── accountMenu/browser/ # Account widget for sidebar footer
├── aiCustomizationManagement/browser/ # AI customization management editor
├── agentFeedback/browser/ # Agent feedback attachments, overlays, hover
├── aiCustomizationTreeView/browser/ # AI customization tree view sidebar
├── applyToParentRepo/browser/ # Apply changes to parent repo
├── changesView/browser/ # File changes view
├── chat/browser/ # Chat actions (run script, branch, prompts)
├── configuration/browser/ # Configuration overrides
── sessions/browser/ # Sessions view, title bar widget, active session service
── files/browser/ # File-related contributions
├── fileTreeView/browser/ # File tree view (filesystem provider)
├── gitSync/browser/ # Git sync contributions
├── logs/browser/ # Log contributions
├── sessions/browser/ # Sessions view, title bar widget, active session service
├── terminal/browser/ # Terminal contributions
├── welcome/browser/ # Welcome view contribution
└── workspace/browser/ # Workspace contributions
```
## 4. Layout
@@ -165,18 +176,21 @@ The agent sessions window uses **its own menu IDs** defined in `browser/menus.ts
| Menu ID | Purpose |
|---------|---------|
| `Menus.TitleBarLeft` | Left toolbar (toggle sidebar) |
| `Menus.TitleBarCenter` | Not used directly (see CommandCenter) |
| `Menus.TitleBarRight` | Right toolbar (run script, open, toggle auxiliary bar) |
| `Menus.ChatBarTitle` | Chat bar title actions |
| `Menus.CommandCenter` | Center toolbar with session picker widget |
| `Menus.TitleBarControlMenu` | Submenu intercepted to render `SessionsTitleBarWidget` |
| `Menus.CommandCenterCenter` | Center section of command center |
| `Menus.TitleBarContext` | Titlebar context menu |
| `Menus.TitleBarLeftLayout` | Left layout toolbar |
| `Menus.TitleBarSessionTitle` | Session title in titlebar |
| `Menus.TitleBarSessionMenu` | Session menu in titlebar |
| `Menus.TitleBarRightLayout` | Right layout toolbar |
| `Menus.PanelTitle` | Panel title bar actions |
| `Menus.SidebarTitle` | Sidebar title bar actions |
| `Menus.SidebarFooter` | Sidebar footer (account widget) |
| `Menus.SidebarCustomizations` | Sidebar customizations menu |
| `Menus.AuxiliaryBarTitle` | Auxiliary bar title actions |
| `Menus.AuxiliaryBarTitleLeft` | Auxiliary bar left title actions |
| `Menus.OpenSubMenu` | "Open..." split button (Open Terminal, Open in VS Code) |
| `Menus.ChatBarTitle` | Chat bar title actions |
| `Menus.AgentFeedbackEditorContent` | Agent feedback editor content menu |
## 7. Context Keys
@@ -187,7 +201,7 @@ Defined in `common/contextkeys.ts`:
| `activeChatBar` | `string` | ID of the active chat bar panel |
| `chatBarFocus` | `boolean` | Whether chat bar has keyboard focus |
| `chatBarVisible` | `boolean` | Whether chat bar is visible |
| `sessionsWelcomeVisible` | `boolean` | Whether the sessions welcome overlay is visible |
## 8. Contributions
Feature contributions live under `contrib/<featureName>/browser/` and are registered via imports in `sessions.desktop.main.ts` (desktop) or `sessions.common.main.ts` (browser-compatible).
@@ -199,13 +213,18 @@ Feature contributions live under `contrib/<featureName>/browser/` and are regist
| **Sessions View** | `contrib/sessions/browser/` | Sessions list in sidebar, session picker, active session service |
| **Title Bar Widget** | `contrib/sessions/browser/sessionsTitleBarWidget.ts` | Session picker in titlebar center |
| **Account Widget** | `contrib/accountMenu/browser/` | Account button in sidebar footer |
| **Run Script** | `contrib/chat/browser/runScriptAction.ts` | Run configured script in terminal |
| **Branch Chat Session** | `contrib/chat/browser/branchChatSessionAction.ts` | Branch a chat session |
| **Open in VS Code / Terminal** | `contrib/chat/browser/chat.contribution.ts` | Open worktree in VS Code or terminal |
| **Prompts Service** | `contrib/chat/browser/promptsService.ts` | Agentic prompts service override |
| **Chat Actions** | `contrib/chat/browser/` | Chat actions (run script, branch, prompts, customizations debug log) |
| **Changes View** | `contrib/changesView/browser/` | File changes in auxiliary bar |
| **AI Customization Editor** | `contrib/aiCustomizationManagement/browser/` | Management editor for prompts, hooks, MCP, etc. |
| **Agent Feedback** | `contrib/agentFeedback/browser/` | Agent feedback attachments, editor overlays, hover |
| **AI Customization Tree** | `contrib/aiCustomizationTreeView/browser/` | Sidebar tree for AI customizations |
| **Apply to Parent Repo** | `contrib/applyToParentRepo/browser/` | Apply changes to parent repo |
| **Files** | `contrib/files/browser/` | File-related contributions |
| **File Tree View** | `contrib/fileTreeView/browser/` | File tree view (filesystem provider) |
| **Git Sync** | `contrib/gitSync/browser/` | Git sync contributions |
| **Logs** | `contrib/logs/browser/` | Log contributions |
| **Terminal** | `contrib/terminal/browser/` | Terminal contributions |
| **Welcome** | `contrib/welcome/browser/` | Welcome view contribution |
| **Workspace** | `contrib/workspace/browser/` | Workspace contributions |
| **Configuration** | `contrib/configuration/browser/` | Configuration overrides |
### 8.2 Service Overrides
@@ -216,6 +235,10 @@ The agent sessions window registers its own implementations for:
- `IPromptsService``AgenticPromptsService` (scopes prompt discovery to active session worktree)
- `IActiveSessionService``ActiveSessionService` (tracks active session)
Service overrides also live under `services/`:
- `services/configuration/browser/` - configuration service overrides
- `services/workspace/browser/` - workspace service overrides
### 8.3 `WindowVisibility.Sessions`
Views and contributions that should only appear in the agent sessions window (not in regular VS Code) use `WindowVisibility.Sessions` in their registration.
@@ -224,12 +247,14 @@ Views and contributions that should only appear in the agent sessions window (no
| File | Purpose |
|------|---------|
| `sessions.common.main.ts` | Common entry imports browser-compatible services, workbench contributions |
| `sessions.desktop.main.ts` | Desktop entry imports desktop services, electron contributions, all `contrib/` modules |
| `sessions.common.main.ts` | Common entry; imports browser-compatible services, workbench contributions |
| `sessions.desktop.main.ts` | Desktop entry; imports desktop services, electron contributions, all `contrib/` modules |
| `electron-browser/sessions.main.ts` | Desktop bootstrap |
| `electron-browser/sessions.ts` | Electron process entry |
| `electron-browser/sessions.html` | Production HTML shell |
| `electron-browser/sessions-dev.html` | Development HTML shell |
| `electron-browser/titleService.ts` | Desktop title service override |
| `electron-browser/parts/titlebarPart.ts` | Desktop titlebar part |
## 10. Development Guidelines
@@ -243,7 +268,13 @@ Views and contributions that should only appear in the agent sessions window (no
6. Use agent session part classes, not standard workbench parts
7. Mark views with `WindowVisibility.Sessions` so they only appear in this window
### 10.2 Layout Changes
### 10.2 Validating Changes
1. Run `npm run compile-check-ts-native` to run a repo-wide TypeScript compilation check (including `src/vs/sessions/`). This is a fast way to catch TypeScript errors introduced by your changes.
2. Run `npm run valid-layers-check` to verify layering rules are not violated.
3. Run tests under `src/vs/sessions/test/` to confirm nothing is broken.
### 10.3 Layout Changes
1. **Read `LAYOUT.md` first** — it's the authoritative spec
2. Use the `agent-sessions-layout` skill for detailed implementation guidance
+3 -3
View File
@@ -212,7 +212,7 @@ jobs:
if: always()
- name: Publish Crash Reports
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: failure()
continue-on-error: true
with:
@@ -223,7 +223,7 @@ jobs:
# In order to properly symbolify above crash reports
# (if any), we need the compiled native modules too
- name: Publish Node Modules
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: failure()
continue-on-error: true
with:
@@ -232,7 +232,7 @@ jobs:
if-no-files-found: ignore
- name: Publish Log Files
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: always()
continue-on-error: true
with:
+3 -3
View File
@@ -258,7 +258,7 @@ jobs:
if: always()
- name: Publish Crash Reports
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: failure()
continue-on-error: true
with:
@@ -269,7 +269,7 @@ jobs:
# In order to properly symbolify above crash reports
# (if any), we need the compiled native modules too
- name: Publish Node Modules
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: failure()
continue-on-error: true
with:
@@ -278,7 +278,7 @@ jobs:
if-no-files-found: ignore
- name: Publish Log Files
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: always()
continue-on-error: true
with:
+3 -3
View File
@@ -249,7 +249,7 @@ jobs:
if: always()
- name: Publish Crash Reports
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: failure()
continue-on-error: true
with:
@@ -260,7 +260,7 @@ jobs:
# In order to properly symbolify above crash reports
# (if any), we need the compiled native modules too
- name: Publish Node Modules
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: failure()
continue-on-error: true
with:
@@ -269,7 +269,7 @@ jobs:
if-no-files-found: ignore
- name: Publish Log Files
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
if: always()
continue-on-error: true
with:
+1 -1
View File
@@ -77,7 +77,7 @@ jobs:
working-directory: build
- name: Compile & Hygiene
run: npm exec -- npm-run-all2 -lp core-ci extensions-ci hygiene eslint valid-layers-check define-class-fields-check vscode-dts-compile-check tsec-compile-check test-build-scripts
run: npm exec -- npm-run-all2 -lp core-ci hygiene eslint valid-layers-check define-class-fields-check vscode-dts-compile-check tsec-compile-check test-build-scripts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+9 -30
View File
@@ -1,4 +1,4 @@
name: Screenshot Tests
name: Checking Component Screenshots
on:
push:
@@ -10,8 +10,6 @@ on:
permissions:
contents: read
pull-requests: write
checks: write
statuses: write
concurrency:
@@ -20,15 +18,16 @@ concurrency:
jobs:
screenshots:
name: Checking Component Screenshots
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
lfs: true
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
@@ -74,14 +73,14 @@ jobs:
fi
- name: Upload explorer artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: component-explorer
path: /tmp/explorer-artifact/
- name: Upload screenshot report
if: steps.compare.outcome == 'failure'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: screenshot-diff
path: |
@@ -95,39 +94,19 @@ jobs:
REPORT="test/componentFixtures/.screenshots/report/report.json"
if [ -f "$REPORT" ]; then
CHANGED=$(node -e "const r = require('./$REPORT'); console.log(r.summary.added + r.summary.removed + r.summary.changed)")
TITLE="${CHANGED} screenshots changed"
TITLE="${CHANGED} screenshots changed"
else
TITLE="Screenshots match"
TITLE="Screenshots match"
fi
SHA="${{ github.event.pull_request.head.sha || github.sha }}"
CHECK_RUN_ID=$(gh api "repos/${{ github.repository }}/commits/$SHA/check-runs" \
--jq '.check_runs[] | select(.name == "screenshots") | .id')
DETAILS_URL="https://hediet-ghartifactpreview.azurewebsites.net/${{ github.repository }}/run/${{ github.run_id }}/component-explorer/___explorer.html?report=./screenshot-report/report.json"
if [ -n "$CHECK_RUN_ID" ]; then
gh api "repos/${{ github.repository }}/check-runs/$CHECK_RUN_ID" \
-X PATCH --input - <<EOF || echo "::warning::Could not update check run (expected for fork PRs)"
{"details_url":"$DETAILS_URL","output":{"title":"$TITLE","summary":"$TITLE"}}
EOF
fi
DETAILS_URL="https://hediet-ghartifactpreview.azurewebsites.net/${{ github.repository }}/run/${{ github.run_id }}/component-explorer/___explorer.html?report=./screenshot-report/report.json"
gh api "repos/${{ github.repository }}/statuses/$SHA" \
--input - <<EOF || echo "::warning::Could not create commit status (expected for fork PRs)"
{"state":"success","target_url":"$DETAILS_URL","description":"$TITLE","context":"screenshots / explorer"}
{"state":"success","target_url":"$DETAILS_URL","description":"$TITLE","context":"Component Screenshots"}
EOF
- name: Post summary
run: |
if [ -f test/componentFixtures/.screenshots/report/report.md ]; then
cat test/componentFixtures/.screenshots/report/report.md >> $GITHUB_STEP_SUMMARY
else
echo "## Screenshots ✅" >> $GITHUB_STEP_SUMMARY
echo "No visual changes detected." >> $GITHUB_STEP_SUMMARY
fi
# - name: Post PR comment
# if: github.event_name == 'pull_request'
# env:
+5 -1
View File
@@ -4,11 +4,15 @@
"recommendations": [
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig",
"github.vscode-pull-request-github",
"ms-vscode.vscode-github-issue-notebooks",
"ms-vscode.extension-test-runner",
"jrieken.vscode-pr-pinger",
"typescriptteam.native-preview",
"ms-vscode.ts-customized-language-service"
],
"stronglyRecommended": [
"github.vscode-pull-request-github",
"ms-vscode.vscode-extras",
"ms-vscode.vscode-selfhost-test-provider"
]
}
+16
View File
@@ -0,0 +1,16 @@
{
"name": "vscode-extras",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vscode-extras",
"version": "0.0.1",
"license": "MIT",
"engines": {
"vscode": "^1.88.0"
}
}
}
}
+38
View File
@@ -0,0 +1,38 @@
{
"name": "vscode-extras",
"displayName": "VS Code Extras",
"description": "Extra utility features for the VS Code selfhost workspace",
"engines": {
"vscode": "^1.88.0"
},
"version": "0.0.1",
"publisher": "ms-vscode",
"categories": [
"Other"
],
"activationEvents": [
"workspaceContains:src/vscode-dts/vscode.d.ts"
],
"main": "./out/extension.js",
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"
},
"license": "MIT",
"scripts": {
"compile": "gulp compile-extension:vscode-extras",
"watch": "gulp watch-extension:vscode-extras"
},
"contributes": {
"configuration": {
"title": "VS Code Extras",
"properties": {
"vscode-extras.npmUpToDateFeature.enabled": {
"type": "boolean",
"default": true,
"description": "Show a status bar warning when npm dependencies are out of date."
}
}
}
}
}
+51
View File
@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { NpmUpToDateFeature } from './npmUpToDateFeature';
export class Extension extends vscode.Disposable {
private readonly _output: vscode.LogOutputChannel;
private _npmFeature: NpmUpToDateFeature | undefined;
constructor(_context: vscode.ExtensionContext) {
const disposables: vscode.Disposable[] = [];
super(() => disposables.forEach(d => d.dispose()));
this._output = vscode.window.createOutputChannel('VS Code Extras', { log: true });
disposables.push(this._output);
this._updateNpmFeature();
disposables.push(
vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('vscode-extras.npmUpToDateFeature.enabled')) {
this._updateNpmFeature();
}
})
);
}
private _updateNpmFeature(): void {
const enabled = vscode.workspace.getConfiguration('vscode-extras').get<boolean>('npmUpToDateFeature.enabled', true);
if (enabled && !this._npmFeature) {
this._npmFeature = new NpmUpToDateFeature(this._output);
} else if (!enabled && this._npmFeature) {
this._npmFeature.dispose();
this._npmFeature = undefined;
}
}
}
let extension: Extension | undefined;
export function activate(context: vscode.ExtensionContext) {
extension = new Extension(context);
context.subscriptions.push(extension);
}
export function deactivate() {
extension = undefined;
}
@@ -0,0 +1,260 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as cp from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
interface FileHashes {
readonly [relativePath: string]: string;
}
interface PostinstallState {
readonly nodeVersion: string;
readonly fileHashes: FileHashes;
}
interface InstallState {
readonly root: string;
readonly stateContentsFile: string;
readonly current: PostinstallState;
readonly saved: PostinstallState | undefined;
readonly files: readonly string[];
}
export class NpmUpToDateFeature extends vscode.Disposable {
private readonly _statusBarItem: vscode.StatusBarItem;
private readonly _disposables: vscode.Disposable[] = [];
private _watchers: fs.FSWatcher[] = [];
private _terminal: vscode.Terminal | undefined;
private _stateContentsFile: string | undefined;
private _root: string | undefined;
private static readonly _scheme = 'npm-dep-state';
constructor(private readonly _output: vscode.LogOutputChannel) {
const disposables: vscode.Disposable[] = [];
super(() => {
disposables.forEach(d => d.dispose());
for (const w of this._watchers) {
w.close();
}
});
this._disposables = disposables;
this._statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10000);
this._statusBarItem.name = 'npm Install State';
this._statusBarItem.text = '$(warning) node_modules is stale - run npm i';
this._statusBarItem.tooltip = 'Dependencies are out of date. Click to run npm install.';
this._statusBarItem.command = 'vscode-extras.runNpmInstall';
this._statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
this._disposables.push(this._statusBarItem);
this._disposables.push(
vscode.workspace.registerTextDocumentContentProvider(NpmUpToDateFeature._scheme, {
provideTextDocumentContent: (uri) => {
const params = new URLSearchParams(uri.query);
const source = params.get('source');
const file = uri.path.slice(1); // strip leading /
if (source === 'saved') {
return this._readSavedContent(file);
}
return this._readCurrentContent(file);
}
})
);
this._disposables.push(
vscode.commands.registerCommand('vscode-extras.runNpmInstall', () => this._runNpmInstall())
);
this._disposables.push(
vscode.commands.registerCommand('vscode-extras.showDependencyDiff', (file: string) => this._showDiff(file))
);
this._disposables.push(
vscode.window.onDidCloseTerminal(t => {
if (t === this._terminal) {
this._terminal = undefined;
this._check();
}
})
);
this._check();
}
private _runNpmInstall(): void {
if (this._terminal) {
this._terminal.dispose();
}
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri;
if (!workspaceRoot) {
return;
}
this._terminal = vscode.window.createTerminal({ name: 'npm install', cwd: workspaceRoot });
this._terminal.sendText('node build/npm/fast-install.ts --force');
this._terminal.show();
this._statusBarItem.text = '$(loading~spin) npm i';
this._statusBarItem.tooltip = 'npm install is running...';
this._statusBarItem.backgroundColor = undefined;
this._statusBarItem.command = 'vscode-extras.runNpmInstall';
}
private _queryState(): InstallState | undefined {
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
if (!workspaceRoot) {
return undefined;
}
try {
const script = path.join(workspaceRoot, 'build', 'npm', 'installStateHash.ts');
const output = cp.execFileSync(process.execPath, [script], {
cwd: workspaceRoot,
timeout: 10_000,
encoding: 'utf8',
});
const parsed = JSON.parse(output.trim());
this._output.trace('raw output:', output.trim());
return parsed;
} catch (e) {
this._output.error('_queryState error:', e as any);
return undefined;
}
}
private _check(): void {
const state = this._queryState();
this._output.trace('state:', JSON.stringify(state, null, 2));
if (!state) {
this._output.trace('no state, hiding');
this._statusBarItem.hide();
return;
}
this._stateContentsFile = state.stateContentsFile;
this._root = state.root;
this._setupWatcher(state);
const changedFiles = this._getChangedFiles(state);
this._output.trace('changedFiles:', JSON.stringify(changedFiles));
if (changedFiles.length === 0) {
this._statusBarItem.hide();
} else {
this._statusBarItem.text = '$(warning) node_modules is stale - run npm i';
const tooltip = new vscode.MarkdownString();
tooltip.isTrusted = true;
tooltip.supportHtml = true;
tooltip.appendMarkdown('**Dependencies are out of date.** Click to run npm install.\n\nChanged files:\n\n');
for (const entry of changedFiles) {
if (entry.isFile) {
const args = encodeURIComponent(JSON.stringify(entry.label));
tooltip.appendMarkdown(`- [${entry.label}](command:vscode-extras.showDependencyDiff?${args})\n`);
} else {
tooltip.appendMarkdown(`- ${entry.label}\n`);
}
}
this._statusBarItem.tooltip = tooltip;
this._statusBarItem.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground');
this._statusBarItem.show();
}
}
private _showDiff(file: string): void {
const cacheBuster = Date.now().toString();
const savedUri = vscode.Uri.from({
scheme: NpmUpToDateFeature._scheme,
path: `/${file}`,
query: new URLSearchParams({ source: 'saved', t: cacheBuster }).toString(),
});
const currentUri = vscode.Uri.from({
scheme: NpmUpToDateFeature._scheme,
path: `/${file}`,
query: new URLSearchParams({ source: 'current', t: cacheBuster }).toString(),
});
vscode.commands.executeCommand('vscode.diff', savedUri, currentUri, `${file} (last install ↔ current)`);
}
private _readSavedContent(file: string): string {
if (!this._stateContentsFile) {
return '';
}
try {
const contents: Record<string, string> = JSON.parse(fs.readFileSync(this._stateContentsFile, 'utf8'));
return contents[file] ?? '';
} catch {
return '';
}
}
private _readCurrentContent(file: string): string {
if (!this._root) {
return '';
}
try {
return this._normalizeFileContent(path.join(this._root, file));
} catch {
return '';
}
}
private _normalizeFileContent(filePath: string): string {
const raw = fs.readFileSync(filePath, 'utf8');
if (path.basename(filePath) === 'package.json') {
const json = JSON.parse(raw);
for (const key of NpmUpToDateFeature._packageJsonIgnoredKeys) {
delete json[key];
}
return JSON.stringify(json, null, '\t') + '\n';
}
return raw;
}
private static readonly _packageJsonIgnoredKeys = ['distro'];
private _getChangedFiles(state: InstallState): { readonly label: string; readonly isFile: boolean }[] {
if (!state.saved) {
return [{ label: '(no postinstall state found)', isFile: false }];
}
const changed: { readonly label: string; readonly isFile: boolean }[] = [];
if (state.saved.nodeVersion !== state.current.nodeVersion) {
changed.push({ label: `Node.js version (${state.saved.nodeVersion}${state.current.nodeVersion})`, isFile: false });
}
const allKeys = new Set([...Object.keys(state.current.fileHashes), ...Object.keys(state.saved.fileHashes)]);
for (const key of allKeys) {
if (state.current.fileHashes[key] !== state.saved.fileHashes[key]) {
changed.push({ label: key, isFile: true });
}
}
return changed;
}
private _setupWatcher(state: InstallState): void {
for (const w of this._watchers) {
w.close();
}
this._watchers = [];
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
const scheduleCheck = () => {
if (debounceTimer) {
clearTimeout(debounceTimer);
}
debounceTimer = setTimeout(() => this._check(), 500);
};
for (const file of state.files) {
try {
const watcher = fs.watch(file, scheduleCheck);
this._watchers.push(watcher);
} catch {
// file may not exist yet
}
}
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "../../../extensions/tsconfig.base.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./out",
"types": [
"node"
]
},
"include": [
"src/**/*",
"../../../src/vscode-dts/vscode.d.ts"
]
}
+72 -2
View File
@@ -278,6 +278,51 @@
"hidden": true,
},
},
{
"type": "chrome",
"request": "launch",
"name": "Launch VS Sessions Internal",
"windows": {
"runtimeExecutable": "${workspaceFolder}/scripts/code.bat"
},
"osx": {
"runtimeExecutable": "${workspaceFolder}/scripts/code.sh"
},
"linux": {
"runtimeExecutable": "${workspaceFolder}/scripts/code.sh"
},
"port": 9222,
"timeout": 0,
"env": {
"VSCODE_EXTHOST_WILL_SEND_SOCKET": null,
"VSCODE_SKIP_PRELAUNCH": "1",
"VSCODE_DEV_DEBUG_OBSERVABLES": "1",
},
"cleanUp": "wholeBrowser",
"killBehavior": "polite",
"runtimeArgs": [
"--inspect-brk=5875",
"--no-cached-data",
"--crash-reporter-directory=${workspaceFolder}/.profile-oss/crashes",
// for general runtime freezes: https://github.com/microsoft/vscode/issues/127861#issuecomment-904144910
"--disable-features=CalculateNativeWinOcclusion",
"--disable-extension=vscode.vscode-api-tests",
"--sessions"
],
"userDataDir": "${userHome}/.vscode-oss-sessions-dev",
"webRoot": "${workspaceFolder}",
"cascadeTerminateToConfigurations": [
"Attach to Extension Host"
],
"pauseForSourceMap": false,
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"browserLaunchLocation": "workspace",
"presentation": {
"hidden": true,
},
},
{
// To debug observables you also need the extension "ms-vscode.debug-value-editor"
"type": "chrome",
@@ -603,9 +648,19 @@
}
},
{
"name": "Component Explorer",
"name": "Component Explorer (Edge)",
"type": "msedge",
"port": 9230,
"request": "launch",
"url": "http://localhost:5337/___explorer",
"preLaunchTask": "Launch Component Explorer",
"presentation": {
"group": "1_component_explorer",
"order": 4
}
},
{
"name": "Component Explorer (Chrome)",
"type": "chrome",
"request": "launch",
"url": "http://localhost:5337/___explorer",
"preLaunchTask": "Launch Component Explorer",
@@ -653,6 +708,21 @@
"order": 1
}
},
{
"name": "VS Sessions",
"stopAll": true,
"configurations": [
"Launch VS Sessions Internal",
"Attach to Main Process",
"Attach to Extension Host",
"Attach to Shared Process",
],
"preLaunchTask": "Ensure Prelaunch Dependencies",
"presentation": {
"group": "0_vscode",
"order": 1
}
},
{
"name": "VS Code (Hot Reload)",
"stopAll": true,
+1 -1
View File
@@ -7,7 +7,7 @@
{
"kind": 2,
"language": "github-issues",
"value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce repo:microsoft/vscode-copilot-issues repo:microsoft/vscode-extension-samples\n\n// current milestone name\n$MILESTONE=milestone:\"February 2026\"\n"
"value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce repo:microsoft/vscode-copilot-issues repo:microsoft/vscode-extension-samples\n\n// current milestone name\n$MILESTONE=milestone:\"March 2026\"\n"
},
{
"kind": 1,
+29 -4
View File
@@ -225,8 +225,7 @@
"windows": {
"command": ".\\scripts\\code.bat"
},
"problemMatcher": [],
"inSessions": true
"problemMatcher": []
},
{
"label": "Run Dev Sessions",
@@ -238,6 +237,18 @@
"args": [
"--sessions"
],
"problemMatcher": []
},
{
"label": "Run and Compile Dev Sessions",
"type": "shell",
"command": "npm run transpile-client && ./scripts/code.sh",
"windows": {
"command": "npm run transpile-client && .\\scripts\\code.bat"
},
"args": [
"--sessions"
],
"inSessions": true,
"problemMatcher": []
},
@@ -375,9 +386,23 @@
{
"label": "Launch Component Explorer",
"type": "shell",
"command": "npx component-explorer serve -c ./test/componentFixtures/component-explorer.json",
"command": "npx component-explorer serve -c ./test/componentFixtures/component-explorer.json -vv",
"isBackground": true,
"problemMatcher": []
"problemMatcher": {
"owner": "component-explorer",
"fileLocation": "absolute",
"pattern": {
"regexp": "^\\s*at\\s+(.+?):(\\d+):(\\d+)\\s*$",
"file": 1,
"line": 2,
"column": 3
},
"background": {
"activeOnStart": true,
"beginsPattern": ".*Setting up sessions.*",
"endsPattern": "Redirection server listening on.*"
}
}
}
]
}
@@ -64,15 +64,6 @@ jobs:
KeyVaultName: vscode-build-secrets
SecretsFilter: "github-distro-mixin-password"
- task: DownloadPipelineArtifact@2
inputs:
artifact: Compilation
path: $(Build.ArtifactStagingDirectory)
displayName: Download compilation output
- script: tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz
displayName: Extract compilation output
- script: node build/setup-npm-registry.ts $NPM_REGISTRY
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM Registry
@@ -173,6 +164,11 @@ jobs:
- template: ../common/install-builtin-extensions.yml@self
- script: npm run gulp core-ci
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Compile
- script: |
set -e
TARGET=$([ "$VSCODE_ARCH" == "x64" ] && echo "linux-alpine" || echo "alpine-arm64") # TODO@joaomoreno
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
set -e
cd $BUILD_STAGINGDIRECTORY
mkdir extraction
cd extraction
git clone --depth 1 https://github.com/microsoft/vscode-extension-telemetry.git
git clone --depth 1 https://github.com/microsoft/vscode-chrome-debug-core.git
git clone --depth 1 https://github.com/microsoft/vscode-node-debug2.git
git clone --depth 1 https://github.com/microsoft/vscode-node-debug.git
git clone --depth 1 https://github.com/microsoft/vscode-html-languageservice.git
git clone --depth 1 https://github.com/microsoft/vscode-json-languageservice.git
node $BUILD_SOURCESDIRECTORY/node_modules/.bin/vscode-telemetry-extractor --sourceDir $BUILD_SOURCESDIRECTORY --excludedDir $BUILD_SOURCESDIRECTORY/extensions --outputDir . --applyEndpoints
node $BUILD_SOURCESDIRECTORY/node_modules/.bin/vscode-telemetry-extractor --config $BUILD_SOURCESDIRECTORY/build/azure-pipelines/common/telemetry-config.json -o .
mkdir -p $BUILD_SOURCESDIRECTORY/.build/telemetry
mv declarations-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-core.json
mv config-resolved.json $BUILD_SOURCESDIRECTORY/.build/telemetry/telemetry-extensions.json
cd ..
rm -rf extraction
@@ -0,0 +1,95 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import cp from 'child_process';
import fs from 'fs';
import os from 'os';
import path from 'path';
const BUILD_STAGINGDIRECTORY = process.env.BUILD_STAGINGDIRECTORY ?? fs.mkdtempSync(path.join(os.tmpdir(), 'vscode-telemetry-'));
const BUILD_SOURCESDIRECTORY = process.env.BUILD_SOURCESDIRECTORY ?? path.resolve(import.meta.dirname, '..', '..', '..');
const extractionDir = path.join(BUILD_STAGINGDIRECTORY, 'extraction');
fs.mkdirSync(extractionDir, { recursive: true });
const repos = [
'https://github.com/microsoft/vscode-extension-telemetry.git',
'https://github.com/microsoft/vscode-chrome-debug-core.git',
'https://github.com/microsoft/vscode-node-debug2.git',
'https://github.com/microsoft/vscode-node-debug.git',
'https://github.com/microsoft/vscode-html-languageservice.git',
'https://github.com/microsoft/vscode-json-languageservice.git',
];
for (const repo of repos) {
cp.execSync(`git clone --depth 1 ${repo}`, { cwd: extractionDir, stdio: 'inherit' });
}
const extractor = path.join(BUILD_SOURCESDIRECTORY, 'node_modules', '@vscode', 'telemetry-extractor', 'out', 'extractor.js');
const telemetryConfig = path.join(BUILD_SOURCESDIRECTORY, 'build', 'azure-pipelines', 'common', 'telemetry-config.json');
interface ITelemetryConfigEntry {
eventPrefix: string;
sourceDirs: string[];
excludedDirs: string[];
applyEndpoints: boolean;
patchDebugEvents?: boolean;
}
const pipelineExtensionsPathPrefix = '../../s/extensions/';
const telemetryConfigEntries = JSON.parse(fs.readFileSync(telemetryConfig, 'utf8')) as ITelemetryConfigEntry[];
let hasLocalConfigOverrides = false;
const resolvedTelemetryConfigEntries = telemetryConfigEntries.map(entry => {
const sourceDirs = entry.sourceDirs.map(sourceDir => {
if (!sourceDir.startsWith(pipelineExtensionsPathPrefix)) {
return sourceDir;
}
const sourceDirInExtractionDir = path.resolve(extractionDir, sourceDir);
if (fs.existsSync(sourceDirInExtractionDir)) {
return sourceDir;
}
const extensionRelativePath = sourceDir.slice(pipelineExtensionsPathPrefix.length);
const sourceDirInWorkspace = path.join(BUILD_SOURCESDIRECTORY, 'extensions', extensionRelativePath);
if (fs.existsSync(sourceDirInWorkspace)) {
hasLocalConfigOverrides = true;
return sourceDirInWorkspace;
}
return sourceDir;
});
return {
...entry,
sourceDirs,
};
});
const telemetryConfigForExtraction = hasLocalConfigOverrides
? path.join(extractionDir, 'telemetry-config.local.json')
: telemetryConfig;
if (hasLocalConfigOverrides) {
fs.writeFileSync(telemetryConfigForExtraction, JSON.stringify(resolvedTelemetryConfigEntries, null, '\t'));
}
try {
cp.execSync(`node "${extractor}" --sourceDir "${BUILD_SOURCESDIRECTORY}" --excludedDir "${path.join(BUILD_SOURCESDIRECTORY, 'extensions')}" --outputDir . --applyEndpoints`, { cwd: extractionDir, stdio: 'inherit' });
cp.execSync(`node "${extractor}" --config "${telemetryConfigForExtraction}" -o .`, { cwd: extractionDir, stdio: 'inherit' });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error(`Telemetry extraction failed: ${message}`);
process.exit(1);
}
const telemetryDir = path.join(BUILD_SOURCESDIRECTORY, '.build', 'telemetry');
fs.mkdirSync(telemetryDir, { recursive: true });
fs.renameSync(path.join(extractionDir, 'declarations-resolved.json'), path.join(telemetryDir, 'telemetry-core.json'));
fs.renameSync(path.join(extractionDir, 'config-resolved.json'), path.join(telemetryDir, 'telemetry-extensions.json'));
fs.rmSync(extractionDir, { recursive: true, force: true });
+1 -9
View File
@@ -970,15 +970,7 @@ async function main() {
console.log(`\u2705 ${name}`);
}
const stages = new Set<string>(['Compile']);
if (
e('VSCODE_BUILD_STAGE_LINUX') === 'True' ||
e('VSCODE_BUILD_STAGE_MACOS') === 'True' ||
e('VSCODE_BUILD_STAGE_WINDOWS') === 'True'
) {
stages.add('CompileCLI');
}
const stages = new Set<string>(['Quality']);
if (e('VSCODE_BUILD_STAGE_WINDOWS') === 'True') { stages.add('Windows'); }
if (e('VSCODE_BUILD_STAGE_LINUX') === 'True') { stages.add('Linux'); }
+23 -4
View File
@@ -29,18 +29,36 @@ jobs:
name: ${{ parameters.poolName }}
os: ${{ parameters.os }}
timeoutInMinutes: 30
templateContext:
outputs:
- output: pipelineArtifact
targetPath: $(SCREENSHOTS_DIR)
artifactName: screenshots-${{ parameters.name }}
displayName: Publish Screenshots
condition: succeededOrFailed()
continueOnError: true
sbomEnabled: false
variables:
TEST_DIR: $(Build.SourcesDirectory)/test/sanity
LOG_FILE: $(TEST_DIR)/results.xml
SCREENSHOTS_DIR: $(TEST_DIR)/screenshots
DOCKER_CACHE_DIR: $(Pipeline.Workspace)/docker-cache
DOCKER_CACHE_FILE: $(DOCKER_CACHE_DIR)/${{ parameters.container }}.tar
steps:
- checkout: self
fetchDepth: 1
fetchTags: false
sparseCheckoutDirectories: test/sanity .nvmrc
sparseCheckoutDirectories: build/azure-pipelines/config test/sanity .nvmrc
displayName: Checkout test/sanity
- ${{ if eq(parameters.os, 'windows') }}:
- script: mkdir "$(SCREENSHOTS_DIR)"
displayName: Create Screenshots Directory
- ${{ else }}:
- bash: mkdir -p "$(SCREENSHOTS_DIR)"
displayName: Create Screenshots Directory
- ${{ if and(eq(parameters.os, 'windows'), eq(parameters.arch, 'arm64')) }}:
- script: |
@echo off
@@ -101,19 +119,19 @@ jobs:
# Windows
- ${{ if eq(parameters.os, 'windows') }}:
- script: $(TEST_DIR)/scripts/run-win32.cmd -c $(BUILD_COMMIT) -q $(BUILD_QUALITY) -t $(LOG_FILE) -v ${{ parameters.args }}
- script: $(TEST_DIR)/scripts/run-win32.cmd -c $(BUILD_COMMIT) -q $(BUILD_QUALITY) -t $(LOG_FILE) -s $(SCREENSHOTS_DIR) -v ${{ parameters.args }}
workingDirectory: $(TEST_DIR)
displayName: Run Sanity Tests
# macOS
- ${{ if eq(parameters.os, 'macOS') }}:
- bash: $(TEST_DIR)/scripts/run-macOS.sh -c $(BUILD_COMMIT) -q $(BUILD_QUALITY) -t $(LOG_FILE) -v ${{ parameters.args }}
- bash: $(TEST_DIR)/scripts/run-macOS.sh -c $(BUILD_COMMIT) -q $(BUILD_QUALITY) -t $(LOG_FILE) -s $(SCREENSHOTS_DIR) -v ${{ parameters.args }}
workingDirectory: $(TEST_DIR)
displayName: Run Sanity Tests
# Native Linux host
- ${{ if and(eq(parameters.container, ''), eq(parameters.os, 'linux')) }}:
- bash: $(TEST_DIR)/scripts/run-ubuntu.sh -c $(BUILD_COMMIT) -q $(BUILD_QUALITY) -t $(LOG_FILE) -v ${{ parameters.args }}
- bash: $(TEST_DIR)/scripts/run-ubuntu.sh -c $(BUILD_COMMIT) -q $(BUILD_QUALITY) -t $(LOG_FILE) -s $(SCREENSHOTS_DIR) -v ${{ parameters.args }}
workingDirectory: $(TEST_DIR)
displayName: Run Sanity Tests
@@ -141,6 +159,7 @@ jobs:
--quality "$(BUILD_QUALITY)" \
--commit "$(BUILD_COMMIT)" \
--test-results "/root/results.xml" \
--screenshots-dir "/root/screenshots" \
--verbose \
${{ parameters.args }}
workingDirectory: $(TEST_DIR)
@@ -1,86 +0,0 @@
parameters:
- name: VSCODE_BUILD_MACOS
type: boolean
- name: VSCODE_BUILD_MACOS_ARM64
type: boolean
jobs:
- job: macOSCLISign
timeoutInMinutes: 90
templateContext:
outputParentDirectory: $(Build.ArtifactStagingDirectory)/out
outputs:
- ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}:
- output: pipelineArtifact
targetPath: $(Build.ArtifactStagingDirectory)/out/vscode_cli_darwin_x64_cli/vscode_cli_darwin_x64_cli.zip
artifactName: vscode_cli_darwin_x64_cli
displayName: Publish signed artifact with ID vscode_cli_darwin_x64_cli
sbomBuildDropPath: $(Build.ArtifactStagingDirectory)/sign/unsigned_vscode_cli_darwin_x64_cli
sbomPackageName: "VS Code macOS x64 CLI"
sbomPackageVersion: $(Build.SourceVersion)
- ${{ if eq(parameters.VSCODE_BUILD_MACOS_ARM64, true) }}:
- output: pipelineArtifact
targetPath: $(Build.ArtifactStagingDirectory)/out/vscode_cli_darwin_arm64_cli/vscode_cli_darwin_arm64_cli.zip
artifactName: vscode_cli_darwin_arm64_cli
displayName: Publish signed artifact with ID vscode_cli_darwin_arm64_cli
sbomBuildDropPath: $(Build.ArtifactStagingDirectory)/sign/unsigned_vscode_cli_darwin_arm64_cli
sbomPackageName: "VS Code macOS arm64 CLI"
sbomPackageVersion: $(Build.SourceVersion)
steps:
- template: ../common/checkout.yml@self
- task: NodeTool@0
inputs:
versionSource: fromFile
versionFilePath: .nvmrc
- task: AzureKeyVault@2
displayName: "Azure Key Vault: Get Secrets"
inputs:
azureSubscription: vscode
KeyVaultName: vscode-build-secrets
SecretsFilter: "github-distro-mixin-password"
- script: node build/setup-npm-registry.ts $NPM_REGISTRY build
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM Registry
- script: |
set -e
# Set the private NPM registry to the global npmrc file
# so that authentication works for subfolders like build/, remote/, extensions/ etc
# which does not have their own .npmrc file
npm config set registry "$NPM_REGISTRY"
echo "##vso[task.setvariable variable=NPMRC_PATH]$(npm config get userconfig)"
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM
- task: npmAuthenticate@0
inputs:
workingFile: $(NPMRC_PATH)
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM Authentication
- script: |
set -e
for i in {1..5}; do # try 5 times
npm ci && break
if [ $i -eq 5 ]; then
echo "Npm install failed too many times" >&2
exit 1
fi
echo "Npm install failed $i, trying again..."
done
workingDirectory: build
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Install build dependencies
- template: ./steps/product-build-darwin-cli-sign.yml@self
parameters:
VSCODE_CLI_ARTIFACTS:
- ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}:
- unsigned_vscode_cli_darwin_x64_cli
- ${{ if eq(parameters.VSCODE_BUILD_MACOS_ARM64, true) }}:
- unsigned_vscode_cli_darwin_arm64_cli
@@ -9,8 +9,8 @@ parameters:
jobs:
- job: macOSCLI_${{ parameters.VSCODE_ARCH }}
displayName: macOS (${{ upper(parameters.VSCODE_ARCH) }})
timeoutInMinutes: 60
displayName: macOS CLI (${{ upper(parameters.VSCODE_ARCH) }})
timeoutInMinutes: 90
pool:
name: AcesShared
os: macOS
@@ -24,11 +24,12 @@ jobs:
outputs:
- ${{ if not(parameters.VSCODE_CHECK_ONLY) }}:
- output: pipelineArtifact
targetPath: $(Build.ArtifactStagingDirectory)/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli.zip
artifactName: unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli
displayName: Publish unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli artifact
sbomEnabled: false
isProduction: false
targetPath: $(Build.ArtifactStagingDirectory)/out/vscode_cli_darwin_$(VSCODE_ARCH)_cli/vscode_cli_darwin_$(VSCODE_ARCH)_cli.zip
artifactName: vscode_cli_darwin_$(VSCODE_ARCH)_cli
displayName: Publish vscode_cli_darwin_$(VSCODE_ARCH)_cli artifact
sbomBuildDropPath: $(Build.ArtifactStagingDirectory)/sign/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli
sbomPackageName: "VS Code macOS $(VSCODE_ARCH) CLI"
sbomPackageVersion: $(Build.SourceVersion)
steps:
- template: ../common/checkout.yml@self
@@ -83,3 +84,55 @@ jobs:
VSCODE_CLI_ENV:
OPENSSL_LIB_DIR: $(Build.ArtifactStagingDirectory)/openssl/arm64-osx/lib
OPENSSL_INCLUDE_DIR: $(Build.ArtifactStagingDirectory)/openssl/arm64-osx/include
- ${{ if not(parameters.VSCODE_CHECK_ONLY) }}:
- template: ../common/publish-artifact.yml@self
parameters:
targetPath: $(Build.ArtifactStagingDirectory)/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli.zip
artifactName: unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli
displayName: Publish unsigned CLI
sbomEnabled: false
- script: |
set -e
mkdir -p $(Build.ArtifactStagingDirectory)/pkg/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli
cp $(Build.ArtifactStagingDirectory)/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli.zip $(Build.ArtifactStagingDirectory)/pkg/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli.zip
displayName: Prepare CLI for signing
- task: ExtractFiles@1
displayName: Extract unsigned CLI (for SBOM)
inputs:
archiveFilePatterns: $(Build.ArtifactStagingDirectory)/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli.zip
destinationFolder: $(Build.ArtifactStagingDirectory)/sign/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli
- task: UseDotNet@2
inputs:
version: 6.x
- task: EsrpCodeSigning@5
inputs:
UseMSIAuthentication: true
ConnectedServiceName: vscode-esrp
AppRegistrationClientId: $(ESRP_CLIENT_ID)
AppRegistrationTenantId: $(ESRP_TENANT_ID)
AuthAKVName: vscode-esrp
AuthSignCertName: esrp-sign
FolderPath: .
Pattern: noop
displayName: 'Install ESRP Tooling'
- script: node build/azure-pipelines/common/sign.ts $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll sign-darwin $(Build.ArtifactStagingDirectory)/pkg/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli "*.zip"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: ✍️ Codesign
- script: node build/azure-pipelines/common/sign.ts $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll notarize-darwin $(Build.ArtifactStagingDirectory)/pkg/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli "*.zip"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: ✍️ Notarize
- script: |
set -e
mkdir -p $(Build.ArtifactStagingDirectory)/out/vscode_cli_darwin_$(VSCODE_ARCH)_cli
mv $(Build.ArtifactStagingDirectory)/pkg/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli/unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli.zip $(Build.ArtifactStagingDirectory)/out/vscode_cli_darwin_$(VSCODE_ARCH)_cli/vscode_cli_darwin_$(VSCODE_ARCH)_cli.zip
displayName: Rename signed artifact
@@ -1,53 +0,0 @@
parameters:
- name: VSCODE_CLI_ARTIFACTS
type: object
default: []
steps:
- task: UseDotNet@2
inputs:
version: 6.x
- task: EsrpCodeSigning@5
inputs:
UseMSIAuthentication: true
ConnectedServiceName: vscode-esrp
AppRegistrationClientId: $(ESRP_CLIENT_ID)
AppRegistrationTenantId: $(ESRP_TENANT_ID)
AuthAKVName: vscode-esrp
AuthSignCertName: esrp-sign
FolderPath: .
Pattern: noop
displayName: 'Install ESRP Tooling'
- ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}:
- task: DownloadPipelineArtifact@2
displayName: Download ${{ target }}
inputs:
artifact: ${{ target }}
path: $(Build.ArtifactStagingDirectory)/pkg/${{ target }}
- task: ExtractFiles@1
displayName: Extract artifact
inputs:
archiveFilePatterns: $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/*.zip
destinationFolder: $(Build.ArtifactStagingDirectory)/sign/${{ target }}
- script: node build/azure-pipelines/common/sign.ts $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll sign-darwin $(Build.ArtifactStagingDirectory)/pkg "*.zip"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: ✍️ Codesign
- script: node build/azure-pipelines/common/sign.ts $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll notarize-darwin $(Build.ArtifactStagingDirectory)/pkg "*.zip"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: ✍️ Notarize
- ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}:
- script: |
set -e
ASSET_ID=$(echo "${{ target }}" | sed "s/unsigned_//")
mkdir -p $(Build.ArtifactStagingDirectory)/out/$ASSET_ID
mv $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/${{ target }}.zip $(Build.ArtifactStagingDirectory)/out/$ASSET_ID/$ASSET_ID.zip
echo "##vso[task.setvariable variable=ASSET_ID]$ASSET_ID"
displayName: Set asset id variable
@@ -30,15 +30,6 @@ steps:
KeyVaultName: vscode-build-secrets
SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key"
- task: DownloadPipelineArtifact@2
inputs:
artifact: Compilation
path: $(Build.ArtifactStagingDirectory)
displayName: Download compilation output
- script: tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz
displayName: Extract compilation output
- script: node build/setup-npm-registry.ts $NPM_REGISTRY
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM Registry
@@ -112,11 +103,33 @@ steps:
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Create node_modules archive
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- script: npx deemon --detach --wait -- node build/azure-pipelines/common/waitForArtifacts.ts unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Wait for CLI artifact (background)
- script: node build/azure-pipelines/distro/mixin-quality.ts
displayName: Mixin distro quality
- template: ../../common/install-builtin-extensions.yml@self
- script: npm run gulp core-ci
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Compile
- script: node build/azure-pipelines/common/extract-telemetry.ts
displayName: Generate lists of telemetry events
- ${{ if or(eq(parameters.VSCODE_RUN_ELECTRON_TESTS, true), eq(parameters.VSCODE_RUN_BROWSER_TESTS, true), eq(parameters.VSCODE_RUN_REMOTE_TESTS, true)) }}:
- script: |
set -e
npm run compile --prefix test/smoke
npm run compile --prefix test/integration/browser
displayName: Compile test suites (non-OSS)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- script: npm run copy-policy-dto --prefix build && node build/lib/policies/policyGenerator.ts build/lib/policies/policyData.jsonc darwin
displayName: Generate policy definitions
@@ -147,6 +160,11 @@ steps:
displayName: Build server (web)
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- script: npx deemon --attach -- node build/azure-pipelines/common/waitForArtifacts.ts unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Wait for CLI artifact
- task: DownloadPipelineArtifact@2
inputs:
artifact: unsigned_vscode_cli_darwin_$(VSCODE_ARCH)_cli
@@ -5,6 +5,9 @@ parameters:
type: string
- name: VSCODE_TEST_SUITE
type: string
- name: VSCODE_RUN_CHECKS
type: boolean
default: false
jobs:
- job: Linux${{ parameters.VSCODE_TEST_SUITE }}
@@ -43,6 +46,7 @@ jobs:
VSCODE_ARCH: x64
VSCODE_CIBUILD: ${{ parameters.VSCODE_CIBUILD }}
VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }}
VSCODE_RUN_CHECKS: ${{ parameters.VSCODE_RUN_CHECKS }}
${{ if eq(parameters.VSCODE_TEST_SUITE, 'Electron') }}:
VSCODE_RUN_ELECTRON_TESTS: true
${{ if eq(parameters.VSCODE_TEST_SUITE, 'Browser') }}:
@@ -9,7 +9,7 @@ parameters:
jobs:
- job: LinuxCLI_${{ parameters.VSCODE_ARCH }}
displayName: Linux (${{ upper(parameters.VSCODE_ARCH) }})
displayName: Linux CLI (${{ upper(parameters.VSCODE_ARCH) }})
timeoutInMinutes: 60
pool:
name: 1es-ubuntu-22.04-x64
@@ -19,6 +19,9 @@ parameters:
- name: VSCODE_RUN_REMOTE_TESTS
type: boolean
default: false
- name: VSCODE_RUN_CHECKS
type: boolean
default: false
jobs:
- job: Linux_${{ parameters.VSCODE_ARCH }}
@@ -26,6 +29,7 @@ jobs:
timeoutInMinutes: 90
variables:
DISPLAY: ":10"
BUILDS_API_URL: $(System.CollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/
NPM_ARCH: ${{ parameters.NPM_ARCH }}
VSCODE_ARCH: ${{ parameters.VSCODE_ARCH }}
templateContext:
@@ -110,3 +114,4 @@ jobs:
VSCODE_RUN_ELECTRON_TESTS: ${{ parameters.VSCODE_RUN_ELECTRON_TESTS }}
VSCODE_RUN_BROWSER_TESTS: ${{ parameters.VSCODE_RUN_BROWSER_TESTS }}
VSCODE_RUN_REMOTE_TESTS: ${{ parameters.VSCODE_RUN_REMOTE_TESTS }}
VSCODE_RUN_CHECKS: ${{ parameters.VSCODE_RUN_CHECKS }}
@@ -17,6 +17,9 @@ parameters:
- name: VSCODE_RUN_REMOTE_TESTS
type: boolean
default: false
- name: VSCODE_RUN_CHECKS
type: boolean
default: false
steps:
- template: ../../common/checkout.yml@self
@@ -35,15 +38,6 @@ steps:
KeyVaultName: vscode-build-secrets
SecretsFilter: "github-distro-mixin-password"
- task: DownloadPipelineArtifact@2
inputs:
artifact: Compilation
path: $(Build.ArtifactStagingDirectory)
displayName: Download compilation output
- script: tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz
displayName: Extract compilation output
- script: |
set -e
# Start X server
@@ -165,11 +159,34 @@ steps:
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Create node_modules archive
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- script: npx deemon --detach --wait -- node build/azure-pipelines/common/waitForArtifacts.ts $(ARTIFACT_PREFIX)vscode_cli_linux_$(VSCODE_ARCH)_cli
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Wait for CLI artifact (background)
- script: node build/azure-pipelines/distro/mixin-quality.ts
displayName: Mixin distro quality
- template: ../../common/install-builtin-extensions.yml@self
- script: npm run gulp core-ci
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Compile
- ${{ if eq(parameters.VSCODE_ARCH, 'x64') }}:
- script: node build/azure-pipelines/common/extract-telemetry.ts
displayName: Generate lists of telemetry events
- ${{ if or(eq(parameters.VSCODE_RUN_ELECTRON_TESTS, true), eq(parameters.VSCODE_RUN_BROWSER_TESTS, true), eq(parameters.VSCODE_RUN_REMOTE_TESTS, true)) }}:
- script: |
set -e
npm run compile --prefix test/smoke
npm run compile --prefix test/integration/browser
displayName: Compile test suites (non-OSS)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- script: npm run copy-policy-dto --prefix build && node build/lib/policies/policyGenerator.ts build/lib/policies/policyData.jsonc linux
displayName: Generate policy definitions
@@ -187,6 +204,11 @@ steps:
displayName: Build client
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- script: npx deemon --attach -- node build/azure-pipelines/common/waitForArtifacts.ts $(ARTIFACT_PREFIX)vscode_cli_linux_$(VSCODE_ARCH)_cli
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Wait for CLI artifact
- task: DownloadPipelineArtifact@2
inputs:
artifact: $(ARTIFACT_PREFIX)vscode_cli_linux_$(VSCODE_ARCH)_cli
@@ -1,106 +0,0 @@
pr: none
trigger: none
parameters:
- name: VSCODE_QUALITY
displayName: Quality
type: string
default: insider
- name: NPM_REGISTRY
displayName: "Custom NPM Registry"
type: string
default: 'https://pkgs.dev.azure.com/monacotools/Monaco/_packaging/vscode/npm/registry/'
- name: CARGO_REGISTRY
displayName: "Custom Cargo Registry"
type: string
default: 'sparse+https://pkgs.dev.azure.com/monacotools/Monaco/_packaging/vscode/Cargo/index/'
variables:
- name: NPM_REGISTRY
${{ if in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}: # disable terrapin when in VSCODE_CIBUILD
value: none
${{ else }}:
value: ${{ parameters.NPM_REGISTRY }}
- name: CARGO_REGISTRY
value: ${{ parameters.CARGO_REGISTRY }}
- name: VSCODE_QUALITY
value: ${{ parameters.VSCODE_QUALITY }}
- name: VSCODE_CIBUILD
value: ${{ in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}
- name: VSCODE_STEP_ON_IT
value: false
- name: skipComponentGovernanceDetection
value: true
- name: ComponentDetection.Timeout
value: 600
- name: Codeql.SkipTaskAutoInjection
value: true
- name: ARTIFACT_PREFIX
value: ''
name: "$(Date:yyyyMMdd).$(Rev:r) (${{ parameters.VSCODE_QUALITY }})"
resources:
repositories:
- repository: 1esPipelines
type: git
name: 1ESPipelineTemplates/1ESPipelineTemplates
ref: refs/tags/release
extends:
template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines
parameters:
sdl:
tsa:
enabled: true
configFile: $(Build.SourcesDirectory)/build/azure-pipelines/config/tsaoptions.json
codeql:
runSourceLanguagesInSourceAnalysis: true
compiled:
enabled: false
justificationForDisabling: "CodeQL breaks ESRP CodeSign on macOS (ICM #520035761, githubcustomers/microsoft-codeql-support#198)"
credscan:
suppressionsFile: $(Build.SourcesDirectory)/build/azure-pipelines/config/CredScanSuppressions.json
eslint:
enabled: true
enableExclusions: true
exclusionsFilePath: $(Build.SourcesDirectory)/.eslint-ignore
sourceAnalysisPool: 1es-windows-2022-x64
createAdoIssuesForJustificationsForDisablement: false
containers:
ubuntu-2004-arm64:
image: onebranch.azurecr.io/linux/ubuntu-2004-arm64:latest
stages:
- stage: Compile
pool:
name: AcesShared
os: macOS
demands:
- ImageOverride -equals ACES_VM_SharedPool_Sequoia
jobs:
- template: build/azure-pipelines/product-compile.yml@self
- stage: macOS
dependsOn:
- Compile
pool:
name: AcesShared
os: macOS
demands:
- ImageOverride -equals ACES_VM_SharedPool_Sequoia
variables:
BUILDSECMON_OPT_IN: true
jobs:
- template: build/azure-pipelines/darwin/product-build-darwin-ci.yml@self
parameters:
VSCODE_CIBUILD: true
VSCODE_TEST_SUITE: Electron
- template: build/azure-pipelines/darwin/product-build-darwin-ci.yml@self
parameters:
VSCODE_CIBUILD: true
VSCODE_TEST_SUITE: Browser
- template: build/azure-pipelines/darwin/product-build-darwin-ci.yml@self
parameters:
VSCODE_CIBUILD: true
VSCODE_TEST_SUITE: Remote
+102 -135
View File
@@ -26,6 +26,13 @@ parameters:
- exploration
- insider
- stable
- name: VSCODE_BUILD_TYPE
displayName: Build Type
type: string
default: Product
values:
- Product
- CI
- name: NPM_REGISTRY
displayName: "Custom NPM Registry"
type: string
@@ -90,10 +97,6 @@ parameters:
displayName: "Release build if successful"
type: boolean
default: false
- name: VSCODE_COMPILE_ONLY
displayName: "Run Compile stage exclusively"
type: boolean
default: false
- name: VSCODE_STEP_ON_IT
displayName: "Skip tests"
type: boolean
@@ -119,9 +122,9 @@ variables:
- name: VSCODE_BUILD_STAGE_WEB
value: ${{ eq(parameters.VSCODE_BUILD_WEB, true) }}
- name: VSCODE_CIBUILD
value: ${{ in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}
value: ${{ or(in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'), eq(parameters.VSCODE_BUILD_TYPE, 'CI')) }}
- name: VSCODE_PUBLISH
value: ${{ and(eq(parameters.VSCODE_PUBLISH, true), eq(variables.VSCODE_CIBUILD, false), eq(parameters.VSCODE_COMPILE_ONLY, false)) }}
value: ${{ and(eq(parameters.VSCODE_PUBLISH, true), eq(variables.VSCODE_CIBUILD, false)) }}
- name: VSCODE_SCHEDULEDBUILD
value: ${{ eq(variables['Build.Reason'], 'Schedule') }}
- name: VSCODE_STEP_ON_IT
@@ -190,27 +193,21 @@ extends:
ubuntu-2004-arm64:
image: onebranch.azurecr.io/linux/ubuntu-2004-arm64:latest
stages:
- stage: Compile
pool:
name: AcesShared
os: macOS
demands:
- ImageOverride -equals ACES_VM_SharedPool_Sequoia
jobs:
- template: build/azure-pipelines/product-compile.yml@self
- ${{ if eq(variables['VSCODE_PUBLISH'], 'true') }}:
- stage: ValidationChecks
- stage: Quality
dependsOn: []
pool:
name: 1es-ubuntu-22.04-x64
os: linux
jobs:
- template: build/azure-pipelines/product-quality-checks.yml@self
- ${{ if eq(variables['VSCODE_BUILD_STAGE_WINDOWS'], true) }}:
- stage: Windows
dependsOn: []
pool:
name: 1es-ubuntu-22.04-x64
os: linux
jobs:
- template: build/azure-pipelines/product-validation-checks.yml@self
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
- stage: CompileCLI
dependsOn: []
name: 1es-windows-2022-x64
os: windows
jobs:
- ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}:
- template: build/azure-pipelines/win32/product-build-win32-cli.yml@self
@@ -225,88 +222,6 @@ extends:
VSCODE_ARCH: arm64
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}:
- template: build/azure-pipelines/linux/product-build-linux-cli.yml@self
parameters:
VSCODE_ARCH: x64
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }}:
- template: build/azure-pipelines/linux/product-build-linux-cli.yml@self
parameters:
VSCODE_ARCH: arm64
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true)) }}:
- template: build/azure-pipelines/linux/product-build-linux-cli.yml@self
parameters:
VSCODE_ARCH: armhf
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}:
- template: build/azure-pipelines/darwin/product-build-darwin-cli.yml@self
parameters:
VSCODE_ARCH: x64
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}:
- template: build/azure-pipelines/darwin/product-build-darwin-cli.yml@self
parameters:
VSCODE_ARCH: arm64
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], true), eq(parameters.VSCODE_COMPILE_ONLY, false)) }}:
- stage: node_modules
dependsOn: []
jobs:
- template: build/azure-pipelines/win32/product-build-win32-node-modules.yml@self
parameters:
VSCODE_ARCH: arm64
- template: build/azure-pipelines/linux/product-build-linux-node-modules.yml@self
parameters:
NPM_ARCH: arm64
VSCODE_ARCH: arm64
- template: build/azure-pipelines/linux/product-build-linux-node-modules.yml@self
parameters:
NPM_ARCH: arm
VSCODE_ARCH: armhf
- template: build/azure-pipelines/alpine/product-build-alpine-node-modules.yml@self
parameters:
VSCODE_ARCH: x64
- template: build/azure-pipelines/alpine/product-build-alpine-node-modules.yml@self
parameters:
VSCODE_ARCH: arm64
- template: build/azure-pipelines/darwin/product-build-darwin-node-modules.yml@self
parameters:
VSCODE_ARCH: x64
- template: build/azure-pipelines/web/product-build-web-node-modules.yml@self
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false)) }}:
- stage: APIScan
dependsOn: []
pool:
name: 1es-windows-2022-x64
os: windows
jobs:
- job: WindowsAPIScan
steps:
- template: build/azure-pipelines/win32/sdl-scan-win32.yml@self
parameters:
VSCODE_ARCH: x64
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_WINDOWS'], true)) }}:
- stage: Windows
dependsOn:
- Compile
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
- CompileCLI
pool:
name: 1es-windows-2022-x64
os: windows
jobs:
- ${{ if eq(variables['VSCODE_CIBUILD'], true) }}:
- template: build/azure-pipelines/win32/product-build-win32-ci.yml@self
parameters:
@@ -341,22 +256,32 @@ extends:
VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }}
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), or(eq(parameters.VSCODE_BUILD_WIN32, true), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true))) }}:
- template: build/azure-pipelines/win32/product-build-win32-cli-sign.yml@self
parameters:
VSCODE_BUILD_WIN32: ${{ parameters.VSCODE_BUILD_WIN32 }}
VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }}
- ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_LINUX'], true)) }}:
- ${{ if eq(variables['VSCODE_BUILD_STAGE_LINUX'], true) }}:
- stage: Linux
dependsOn:
- Compile
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
- CompileCLI
dependsOn: []
pool:
name: 1es-ubuntu-22.04-x64
os: linux
jobs:
- ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}:
- template: build/azure-pipelines/linux/product-build-linux-cli.yml@self
parameters:
VSCODE_ARCH: x64
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }}:
- template: build/azure-pipelines/linux/product-build-linux-cli.yml@self
parameters:
VSCODE_ARCH: arm64
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true)) }}:
- template: build/azure-pipelines/linux/product-build-linux-cli.yml@self
parameters:
VSCODE_ARCH: armhf
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if eq(variables['VSCODE_CIBUILD'], true) }}:
- template: build/azure-pipelines/linux/product-build-linux-ci.yml@self
parameters:
@@ -402,10 +327,9 @@ extends:
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_ALPINE'], true)) }}:
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(variables['VSCODE_BUILD_STAGE_ALPINE'], true)) }}:
- stage: Alpine
dependsOn:
- Compile
dependsOn: []
jobs:
- ${{ if eq(parameters.VSCODE_BUILD_ALPINE, true) }}:
- template: build/azure-pipelines/alpine/product-build-alpine.yml@self
@@ -424,12 +348,9 @@ extends:
VSCODE_ARCH: arm64
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_MACOS'], true)) }}:
- ${{ if eq(variables['VSCODE_BUILD_STAGE_MACOS'], true) }}:
- stage: macOS
dependsOn:
- Compile
- ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}:
- CompileCLI
dependsOn: []
pool:
name: AcesShared
os: macOS
@@ -438,6 +359,19 @@ extends:
variables:
BUILDSECMON_OPT_IN: true
jobs:
- ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}:
- template: build/azure-pipelines/darwin/product-build-darwin-cli.yml@self
parameters:
VSCODE_ARCH: x64
VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }}
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}:
- template: build/azure-pipelines/darwin/product-build-darwin-cli.yml@self
parameters:
VSCODE_ARCH: arm64
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
- ${{ if eq(variables['VSCODE_CIBUILD'], true) }}:
- template: build/azure-pipelines/darwin/product-build-darwin-ci.yml@self
parameters:
@@ -470,20 +404,13 @@ extends:
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(variables['VSCODE_BUILD_MACOS_UNIVERSAL'], true)) }}:
- template: build/azure-pipelines/darwin/product-build-darwin-universal.yml@self
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), or(eq(parameters.VSCODE_BUILD_MACOS, true), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true))) }}:
- template: build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml@self
parameters:
VSCODE_BUILD_MACOS: ${{ parameters.VSCODE_BUILD_MACOS }}
VSCODE_BUILD_MACOS_ARM64: ${{ parameters.VSCODE_BUILD_MACOS_ARM64 }}
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_WEB'], true)) }}:
- ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(variables['VSCODE_BUILD_STAGE_WEB'], true)) }}:
- stage: Web
dependsOn:
- Compile
dependsOn: []
jobs:
- template: build/azure-pipelines/web/product-build-web.yml@self
- ${{ if eq(variables['VSCODE_PUBLISH'], 'true') }}:
- ${{ if eq(variables['VSCODE_PUBLISH'], true) }}:
- stage: Publish
dependsOn: []
jobs:
@@ -811,3 +738,43 @@ extends:
- template: build/azure-pipelines/product-release.yml@self
parameters:
VSCODE_RELEASE: ${{ parameters.VSCODE_RELEASE }}
- ${{ if eq(variables['VSCODE_CIBUILD'], true) }}:
- stage: node_modules
dependsOn: []
jobs:
- template: build/azure-pipelines/win32/product-build-win32-node-modules.yml@self
parameters:
VSCODE_ARCH: arm64
- template: build/azure-pipelines/linux/product-build-linux-node-modules.yml@self
parameters:
NPM_ARCH: arm64
VSCODE_ARCH: arm64
- template: build/azure-pipelines/linux/product-build-linux-node-modules.yml@self
parameters:
NPM_ARCH: arm
VSCODE_ARCH: armhf
- template: build/azure-pipelines/alpine/product-build-alpine-node-modules.yml@self
parameters:
VSCODE_ARCH: x64
- template: build/azure-pipelines/alpine/product-build-alpine-node-modules.yml@self
parameters:
VSCODE_ARCH: arm64
- template: build/azure-pipelines/darwin/product-build-darwin-node-modules.yml@self
parameters:
VSCODE_ARCH: x64
- template: build/azure-pipelines/web/product-build-web-node-modules.yml@self
- ${{ if eq(variables['VSCODE_CIBUILD'], false) }}:
- stage: APIScan
dependsOn: []
pool:
name: 1es-windows-2022-x64
os: windows
jobs:
- job: WindowsAPIScan
steps:
- template: build/azure-pipelines/win32/sdl-scan-win32.yml@self
parameters:
VSCODE_ARCH: x64
VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }}
@@ -1,14 +1,12 @@
jobs:
- job: Compile
timeoutInMinutes: 60
templateContext:
outputs:
- output: pipelineArtifact
targetPath: $(Build.ArtifactStagingDirectory)/compilation.tar.gz
artifactName: Compilation
displayName: Publish compilation artifact
isProduction: false
sbomEnabled: false
- job: Quality
displayName: Quality Checks
timeoutInMinutes: 20
variables:
- name: skipComponentGovernanceDetection
value: true
- name: Codeql.SkipTaskAutoInjection
value: true
steps:
- template: ./common/checkout.yml@self
@@ -30,7 +28,7 @@ jobs:
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM Registry
- script: mkdir -p .build && node build/azure-pipelines/common/computeNodeModulesCacheKey.ts compile $(node -p process.arch) > .build/packagelockhash
- script: mkdir -p .build && node build/azure-pipelines/common/computeNodeModulesCacheKey.ts quality $(node -p process.arch) > .build/packagelockhash
displayName: Prepare node_modules cache key
- task: Cache@2
@@ -46,9 +44,6 @@ jobs:
- script: |
set -e
# Set the private NPM registry to the global npmrc file
# so that authentication works for subfolders like build/, remote/, extensions/ etc
# which does not have their own .npmrc file
npm config set registry "$NPM_REGISTRY"
echo "##vso[task.setvariable variable=NPMRC_PATH]$(npm config get userconfig)"
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none'))
@@ -71,7 +66,38 @@ jobs:
fi
echo "Npm install failed $i, trying again..."
done
workingDirectory: build
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Install build dependencies
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
- script: |
set -e
export VSCODE_SYSROOT_DIR=$(Build.SourcesDirectory)/.build/sysroots/glibc-2.28-gcc-8.5.0
SYSROOT_ARCH="amd64" VSCODE_SYSROOT_PREFIX="-glibc-2.28-gcc-8.5.0" node -e 'import { getVSCodeSysroot } from "./build/linux/debian/install-sysroot.ts"; (async () => { await getVSCodeSysroot(process.env["SYSROOT_ARCH"]); })()'
env:
VSCODE_ARCH: x64
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Download vscode sysroots
- script: |
set -e
source ./build/azure-pipelines/linux/setup-env.sh
node build/npm/preinstall.ts
for i in {1..5}; do # try 5 times
npm ci && break
if [ $i -eq 5 ]; then
echo "Npm install failed too many times" >&2
exit 1
fi
echo "Npm install failed $i, trying again..."
done
env:
npm_config_arch: x64
VSCODE_ARCH: x64
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
GITHUB_TOKEN: "$(github-distro-mixin-password)"
@@ -93,43 +119,37 @@ jobs:
- script: node build/azure-pipelines/distro/mixin-quality.ts
displayName: Mixin distro quality
- template: common/install-builtin-extensions.yml@self
- script: node build/azure-pipelines/common/checkDistroCommit.ts
displayName: Check distro commit
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
BUILD_SOURCEBRANCH: "$(Build.SourceBranch)"
continueOnError: true
condition: and(succeeded(), eq(lower(variables['VSCODE_PUBLISH']), 'true'))
- script: npm exec -- npm-run-all2 -lp core-ci extensions-ci hygiene eslint valid-layers-check define-class-fields-check vscode-dts-compile-check tsec-compile-check test-build-scripts
- script: node build/azure-pipelines/common/checkCopilotChatCompatibility.ts --warn-only
displayName: Check Copilot Chat compatibility
continueOnError: true
condition: and(succeeded(), eq(lower(variables['VSCODE_PUBLISH']), 'true'))
- script: npm exec -- npm-run-all2 -lp core-ci hygiene eslint valid-layers-check define-class-fields-check vscode-dts-compile-check tsec-compile-check test-build-scripts
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Compile & Hygiene
- script: |
set -e
- script: npm run download-builtin-extensions-cg
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Download component details of built-in extensions
condition: and(succeeded(), eq(lower(variables['VSCODE_PUBLISH']), 'true'))
[ -d "out-build" ] || { echo "ERROR: out-build folder is missing" >&2; exit 1; }
[ -n "$(find out-build -mindepth 1 2>/dev/null | head -1)" ] || { echo "ERROR: out-build folder is empty" >&2; exit 1; }
echo "out-build exists and is not empty"
ls -d out-vscode-* >/dev/null 2>&1 || { echo "ERROR: No out-vscode-* folders found" >&2; exit 1; }
for folder in out-vscode-*; do
[ -d "$folder" ] || { echo "ERROR: $folder is missing" >&2; exit 1; }
[ -n "$(find "$folder" -mindepth 1 2>/dev/null | head -1)" ] || { echo "ERROR: $folder is empty" >&2; exit 1; }
echo "$folder exists and is not empty"
done
echo "All required compilation folders checked."
displayName: Validate compilation folders
- script: |
set -e
npm run compile
displayName: Compile smoke test suites (non-OSS)
workingDirectory: test/smoke
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
npm run compile
displayName: Compile integration test suites (non-OSS)
workingDirectory: test/integration/browser
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: "Component Detection"
inputs:
sourceScanPath: $(Build.SourcesDirectory)
alertWarningLevel: Medium
continueOnError: true
condition: and(succeeded(), eq(lower(variables['VSCODE_PUBLISH']), 'true'))
- task: AzureCLI@2
displayName: Fetch secrets
@@ -142,6 +162,7 @@ jobs:
Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId"
Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId"
Write-Host "##vso[task.setvariable variable=AZURE_ID_TOKEN;issecret=true]$env:idToken"
condition: and(succeeded(), eq(lower(variables['VSCODE_PUBLISH']), 'true'))
- script: |
set -e
@@ -151,21 +172,4 @@ jobs:
AZURE_ID_TOKEN="$(AZURE_ID_TOKEN)" \
node build/azure-pipelines/upload-sourcemaps.ts
displayName: Upload sourcemaps to Azure
- script: ./build/azure-pipelines/common/extract-telemetry.sh
displayName: Generate lists of telemetry events
- script: tar -cz --exclude='.build/node_modules_cache' --exclude='.build/node_modules_list.txt' --exclude='.build/distro' -f $(Build.ArtifactStagingDirectory)/compilation.tar.gz $(ls -d .build out-* test/integration/browser/out test/smoke/out test/automation/out 2>/dev/null)
displayName: Compress compilation artifact
- script: npm run download-builtin-extensions-cg
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Download component details of built-in extensions
- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0
displayName: "Component Detection"
inputs:
sourceScanPath: $(Build.SourcesDirectory)
alertWarningLevel: Medium
continueOnError: true
condition: and(succeeded(), eq(lower(variables['VSCODE_PUBLISH']), 'true'))
@@ -1,40 +0,0 @@
jobs:
- job: ValidationChecks
displayName: Distro and Extension Validation
timeoutInMinutes: 15
steps:
- template: ./common/checkout.yml@self
- task: NodeTool@0
inputs:
versionSource: fromFile
versionFilePath: .nvmrc
- template: ./distro/download-distro.yml@self
- script: node build/azure-pipelines/distro/mixin-quality.ts
displayName: Mixin distro quality
- task: AzureKeyVault@2
displayName: "Azure Key Vault: Get Secrets"
inputs:
azureSubscription: vscode
KeyVaultName: vscode-build-secrets
SecretsFilter: "github-distro-mixin-password"
- script: npm ci
workingDirectory: build
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Install build dependencies
- script: node build/azure-pipelines/common/checkDistroCommit.ts
displayName: Check distro commit
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
BUILD_SOURCEBRANCH: "$(Build.SourceBranch)"
continueOnError: true
- script: node build/azure-pipelines/common/checkCopilotChatCompatibility.ts --warn-only
displayName: Check Copilot Chat compatibility
continueOnError: true
@@ -33,15 +33,6 @@ jobs:
KeyVaultName: vscode-build-secrets
SecretsFilter: "github-distro-mixin-password"
- task: DownloadPipelineArtifact@2
inputs:
artifact: Compilation
path: $(Build.ArtifactStagingDirectory)
displayName: Download compilation output
- script: tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz
displayName: Extract compilation output
- script: node build/setup-npm-registry.ts $NPM_REGISTRY
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM Registry
@@ -118,6 +109,11 @@ jobs:
- template: ../common/install-builtin-extensions.yml@self
- script: npm run gulp core-ci
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Compile
- script: |
set -e
npm run gulp vscode-web-min-ci
@@ -1,83 +0,0 @@
parameters:
- name: VSCODE_BUILD_WIN32
type: boolean
- name: VSCODE_BUILD_WIN32_ARM64
type: boolean
jobs:
- job: WindowsCLISign
timeoutInMinutes: 90
templateContext:
outputParentDirectory: $(Build.ArtifactStagingDirectory)/out
outputs:
- ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}:
- output: pipelineArtifact
targetPath: $(Build.ArtifactStagingDirectory)/out/vscode_cli_win32_x64_cli.zip
artifactName: vscode_cli_win32_x64_cli
displayName: Publish signed artifact with ID vscode_cli_win32_x64_cli
sbomBuildDropPath: $(Build.BinariesDirectory)/sign/unsigned_vscode_cli_win32_x64_cli
sbomPackageName: "VS Code Windows x64 CLI"
sbomPackageVersion: $(Build.SourceVersion)
- ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}:
- output: pipelineArtifact
targetPath: $(Build.ArtifactStagingDirectory)/out/vscode_cli_win32_arm64_cli.zip
artifactName: vscode_cli_win32_arm64_cli
displayName: Publish signed artifact with ID vscode_cli_win32_arm64_cli
sbomBuildDropPath: $(Build.BinariesDirectory)/sign/unsigned_vscode_cli_win32_arm64_cli
sbomPackageName: "VS Code Windows arm64 CLI"
sbomPackageVersion: $(Build.SourceVersion)
steps:
- template: ../common/checkout.yml@self
- task: NodeTool@0
displayName: "Use Node.js"
inputs:
versionSource: fromFile
versionFilePath: .nvmrc
- task: AzureKeyVault@2
displayName: "Azure Key Vault: Get Secrets"
inputs:
azureSubscription: vscode
KeyVaultName: vscode-build-secrets
SecretsFilter: "github-distro-mixin-password"
- powershell: node build/setup-npm-registry.ts $env:NPM_REGISTRY build
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM Registry
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
# Set the private NPM registry to the global npmrc file
# so that authentication works for subfolders like build/, remote/, extensions/ etc
# which does not have their own .npmrc file
exec { npm config set registry "$env:NPM_REGISTRY" }
$NpmrcPath = (npm config get userconfig)
echo "##vso[task.setvariable variable=NPMRC_PATH]$NpmrcPath"
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM
- task: npmAuthenticate@0
inputs:
workingFile: $(NPMRC_PATH)
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM Authentication
- powershell: |
. azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { npm ci }
workingDirectory: build
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
retryCountOnTaskFailure: 5
displayName: Install build dependencies
- template: ./steps/product-build-win32-cli-sign.yml@self
parameters:
VSCODE_CLI_ARTIFACTS:
- ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}:
- unsigned_vscode_cli_win32_x64_cli
- ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}:
- unsigned_vscode_cli_win32_arm64_cli
@@ -9,22 +9,23 @@ parameters:
jobs:
- job: WindowsCLI_${{ upper(parameters.VSCODE_ARCH) }}
displayName: Windows (${{ upper(parameters.VSCODE_ARCH) }})
displayName: Windows CLI (${{ upper(parameters.VSCODE_ARCH) }})
pool:
name: 1es-windows-2022-x64
os: windows
timeoutInMinutes: 30
timeoutInMinutes: 90
variables:
VSCODE_ARCH: ${{ parameters.VSCODE_ARCH }}
templateContext:
outputs:
- ${{ if not(parameters.VSCODE_CHECK_ONLY) }}:
- output: pipelineArtifact
targetPath: $(Build.ArtifactStagingDirectory)/unsigned_vscode_cli_win32_$(VSCODE_ARCH)_cli.zip
artifactName: unsigned_vscode_cli_win32_$(VSCODE_ARCH)_cli
displayName: Publish unsigned_vscode_cli_win32_$(VSCODE_ARCH)_cli artifact
sbomEnabled: false
isProduction: false
targetPath: $(Build.ArtifactStagingDirectory)/out/vscode_cli_win32_$(VSCODE_ARCH)_cli.zip
artifactName: vscode_cli_win32_$(VSCODE_ARCH)_cli
displayName: Publish vscode_cli_win32_$(VSCODE_ARCH)_cli artifact
sbomBuildDropPath: $(Build.ArtifactStagingDirectory)/sign
sbomPackageName: "VS Code Windows $(VSCODE_ARCH) CLI"
sbomPackageVersion: $(Build.SourceVersion)
steps:
- template: ../common/checkout.yml@self
@@ -75,3 +76,54 @@ jobs:
${{ if eq(parameters.VSCODE_ARCH, 'arm64') }}:
RUSTFLAGS: "-Ctarget-feature=+crt-static -Clink-args=/guard:cf -Clink-args=/CETCOMPAT:NO"
CFLAGS: "/guard:cf /Qspectre"
- ${{ if not(parameters.VSCODE_CHECK_ONLY) }}:
- template: ../common/publish-artifact.yml@self
parameters:
targetPath: $(Build.ArtifactStagingDirectory)/unsigned_vscode_cli_win32_$(VSCODE_ARCH)_cli.zip
artifactName: unsigned_vscode_cli_win32_$(VSCODE_ARCH)_cli
displayName: Publish unsigned CLI
sbomEnabled: false
- task: ExtractFiles@1
displayName: Extract unsigned CLI
inputs:
archiveFilePatterns: $(Build.ArtifactStagingDirectory)/unsigned_vscode_cli_win32_$(VSCODE_ARCH)_cli.zip
destinationFolder: $(Build.ArtifactStagingDirectory)/sign
- task: UseDotNet@2
inputs:
version: 6.x
- task: EsrpCodeSigning@5
inputs:
UseMSIAuthentication: true
ConnectedServiceName: vscode-esrp
AppRegistrationClientId: $(ESRP_CLIENT_ID)
AppRegistrationTenantId: $(ESRP_TENANT_ID)
AuthAKVName: vscode-esrp
AuthSignCertName: esrp-sign
FolderPath: .
Pattern: noop
displayName: 'Install ESRP Tooling'
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
$EsrpCodeSigningTool = (gci -directory -filter EsrpCodeSigning_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName
$Version = (gci -directory $EsrpCodeSigningTool | Select-Object -last 1).FullName
echo "##vso[task.setvariable variable=EsrpCliDllPath]$Version\net6.0\esrpcli.dll"
displayName: Find ESRP CLI
- powershell: node build\azure-pipelines\common\sign.ts $env:EsrpCliDllPath sign-windows $(Build.ArtifactStagingDirectory)/sign "*.exe"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: ✍️ Codesign
- task: ArchiveFiles@2
displayName: Archive signed CLI
inputs:
rootFolderOrFile: $(Build.ArtifactStagingDirectory)/sign
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/out/vscode_cli_win32_$(VSCODE_ARCH)_cli.zip
@@ -21,6 +21,7 @@ jobs:
timeoutInMinutes: 90
variables:
VSCODE_ARCH: ${{ parameters.VSCODE_ARCH }}
BUILDS_API_URL: $(System.CollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/
templateContext:
outputParentDirectory: $(Build.ArtifactStagingDirectory)/out
outputs:
@@ -1,61 +0,0 @@
parameters:
- name: VSCODE_CLI_ARTIFACTS
type: object
default: []
steps:
- task: UseDotNet@2
inputs:
version: 6.x
- task: EsrpCodeSigning@5
inputs:
UseMSIAuthentication: true
ConnectedServiceName: vscode-esrp
AppRegistrationClientId: $(ESRP_CLIENT_ID)
AppRegistrationTenantId: $(ESRP_TENANT_ID)
AuthAKVName: vscode-esrp
AuthSignCertName: esrp-sign
FolderPath: .
Pattern: noop
displayName: 'Install ESRP Tooling'
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
$EsrpCodeSigningTool = (gci -directory -filter EsrpCodeSigning_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName
$Version = (gci -directory $EsrpCodeSigningTool | Select-Object -last 1).FullName
echo "##vso[task.setvariable variable=EsrpCliDllPath]$Version\net6.0\esrpcli.dll"
displayName: Find ESRP CLI
- ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}:
- task: DownloadPipelineArtifact@2
displayName: Download artifact
inputs:
artifact: ${{ target }}
path: $(Build.BinariesDirectory)/pkg/${{ target }}
- task: ExtractFiles@1
displayName: Extract artifact
inputs:
archiveFilePatterns: $(Build.BinariesDirectory)/pkg/${{ target }}/*.zip
destinationFolder: $(Build.BinariesDirectory)/sign/${{ target }}
- powershell: node build\azure-pipelines\common\sign.ts $env:EsrpCliDllPath sign-windows $(Build.BinariesDirectory)/sign "*.exe"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: ✍️ Codesign
- ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}:
- powershell: |
$ASSET_ID = "${{ target }}".replace("unsigned_", "");
echo "##vso[task.setvariable variable=ASSET_ID]$ASSET_ID"
displayName: Set asset id variable
- task: ArchiveFiles@2
displayName: Archive signed files
inputs:
rootFolderOrFile: $(Build.BinariesDirectory)/sign/${{ target }}
includeRootFolder: false
archiveType: zip
archiveFile: $(Build.ArtifactStagingDirectory)/out/$(ASSET_ID).zip
@@ -37,18 +37,6 @@ steps:
KeyVaultName: vscode-build-secrets
SecretsFilter: "github-distro-mixin-password"
- task: DownloadPipelineArtifact@2
inputs:
artifact: Compilation
path: $(Build.ArtifactStagingDirectory)
displayName: Download compilation output
- task: ExtractFiles@1
displayName: Extract compilation output
inputs:
archiveFilePatterns: "$(Build.ArtifactStagingDirectory)/compilation.tar.gz"
cleanDestinationFolder: false
- powershell: node build/setup-npm-registry.ts $env:NPM_REGISTRY
condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none'))
displayName: Setup NPM Registry
@@ -114,11 +102,34 @@ steps:
condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'))
displayName: Create node_modules archive
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- pwsh: npx deemon --detach --wait -- node build/azure-pipelines/common/waitForArtifacts.ts unsigned_vscode_cli_win32_$(VSCODE_ARCH)_cli
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Wait for CLI artifact (background)
- powershell: node build/azure-pipelines/distro/mixin-quality.ts
displayName: Mixin distro quality
- template: ../../common/install-builtin-extensions.yml@self
- powershell: npm run gulp core-ci
env:
GITHUB_TOKEN: "$(github-distro-mixin-password)"
displayName: Compile
- script: node build/azure-pipelines/common/extract-telemetry.ts
displayName: Generate lists of telemetry events
- ${{ if or(eq(parameters.VSCODE_RUN_ELECTRON_TESTS, true), eq(parameters.VSCODE_RUN_BROWSER_TESTS, true), eq(parameters.VSCODE_RUN_REMOTE_TESTS, true)) }}:
- powershell: |
. build/azure-pipelines/win32/exec.ps1
$ErrorActionPreference = "Stop"
exec { npm run compile --prefix test/smoke }
exec { npm run compile --prefix test/integration/browser }
displayName: Compile test suites (non-OSS)
condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- powershell: |
npm run copy-policy-dto --prefix build
@@ -181,6 +192,11 @@ steps:
displayName: Build server (web)
- ${{ if ne(parameters.VSCODE_CIBUILD, true) }}:
- pwsh: npx deemon --attach -- node build/azure-pipelines/common/waitForArtifacts.ts unsigned_vscode_cli_win32_$(VSCODE_ARCH)_cli
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
displayName: Wait for CLI artifact
- task: DownloadPipelineArtifact@2
inputs:
artifact: unsigned_vscode_cli_win32_$(VSCODE_ARCH)_cli
+1 -1
View File
@@ -28,7 +28,7 @@ async function main(buildDir?: string) {
const filesToSkip = [
'**/CodeResources',
'**/Credits.rtf',
'**/policies/{*.mobileconfig,**/*.plist}',
'**/policies/{*.mobileconfig,**/*.plist}'
];
await makeUniversalApp({
+3 -2
View File
@@ -6,8 +6,9 @@ format = 'ULMO'
badge_icon = {{BADGE_ICON}}
background = {{BACKGROUND}}
# Volume size (None = auto-calculate)
size = None
# Volume size
size = '1g'
shrink = False
# Files and symlinks
files = [{{APP_PATH}}]
+1 -24
View File
@@ -95,6 +95,7 @@ const compilations = [
'.vscode/extensions/vscode-selfhost-test-provider/tsconfig.json',
'.vscode/extensions/vscode-selfhost-import-aid/tsconfig.json',
'.vscode/extensions/vscode-extras/tsconfig.json',
];
const getBaseUrl = (out: string) => `https://main.vscode-cdn.net/sourcemaps/${commit}/${out}`;
@@ -289,19 +290,7 @@ export const compileAllExtensionsBuildTask = task.define('compile-extensions-bui
));
gulp.task(compileAllExtensionsBuildTask);
// This task is run in the compilation stage of the CI pipeline. We only compile the non-native extensions since those can be fully built regardless of platform.
// This defers the native extensions to the platform specific stage of the CI pipeline.
gulp.task(task.define('extensions-ci', task.series(compileNonNativeExtensionsBuildTask, compileExtensionMediaBuildTask)));
const compileExtensionsBuildPullRequestTask = task.define('compile-extensions-build-pr', task.series(
cleanExtensionsBuildTask,
bundleMarketplaceExtensionsBuildTask,
task.define('bundle-extensions-build-pr', () => ext.packageAllLocalExtensionsStream(false, true).pipe(gulp.dest('.build'))),
));
gulp.task(compileExtensionsBuildPullRequestTask);
// This task is run in the compilation stage of the PR pipeline. We compile all extensions in it to verify compilation.
gulp.task(task.define('extensions-ci-pr', task.series(compileExtensionsBuildPullRequestTask, compileExtensionMediaBuildTask)));
//#endregion
@@ -320,13 +309,6 @@ async function buildWebExtensions(isWatch: boolean): Promise<void> {
{ ignore: ['**/node_modules'] }
);
// Find all webpack configs, excluding those that will be esbuilt
const esbuildExtensionDirs = new Set(esbuildConfigLocations.map(p => path.dirname(p)));
const webpackConfigLocations = (await nodeUtil.promisify(glob)(
path.join(extensionsPath, '**', 'extension-browser.webpack.config.js'),
{ ignore: ['**/node_modules'] }
)).filter(configPath => !esbuildExtensionDirs.has(path.dirname(configPath)));
const promises: Promise<unknown>[] = [];
// Esbuild for extensions
@@ -341,10 +323,5 @@ async function buildWebExtensions(isWatch: boolean): Promise<void> {
);
}
// Run webpack for remaining extensions
if (webpackConfigLocations.length > 0) {
promises.push(ext.webpackExtensions('packaging web extension', isWatch, webpackConfigLocations.map(configPath => ({ configPath }))));
}
await Promise.all(promises);
}
+47 -20
View File
@@ -31,6 +31,7 @@ import minimist from 'minimist';
import { compileBuildWithoutManglingTask, compileBuildWithManglingTask } from './gulpfile.compile.ts';
import { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask } from './gulpfile.extensions.ts';
import { copyCodiconsTask } from './lib/compilation.ts';
import type { EmbeddedProductInfo } from './lib/embeddedType.ts';
import { useEsbuildTranspile } from './buildConfig.ts';
import { promisify } from 'util';
import globCallback from 'glob';
@@ -236,6 +237,9 @@ function runTsGoTypeCheck(): Promise<void> {
}
const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;
const isCI = !!process.env['CI'] || !!process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] || !!process.env['GITHUB_WORKSPACE'];
const useCdnSourceMapsForPackagingTasks = isCI;
const stripSourceMapsInPackagingTasks = isCI;
const minifyVSCodeTask = task.define('minify-vscode', task.series(
bundleVSCodeTask,
util.rimraf('out-vscode-min'),
@@ -243,19 +247,17 @@ const minifyVSCodeTask = task.define('minify-vscode', task.series(
));
gulp.task(minifyVSCodeTask);
const coreCIOld = task.define('core-ci-old', task.series(
gulp.task(task.define('core-ci-old', task.series(
gulp.task('compile-build-with-mangling') as task.Task,
task.parallel(
gulp.task('minify-vscode') as task.Task,
gulp.task('minify-vscode-reh') as task.Task,
gulp.task('minify-vscode-reh-web') as task.Task,
)
));
gulp.task(coreCIOld);
)));
const coreCIEsbuild = task.define('core-ci-esbuild', task.series(
gulp.task(task.define('core-ci', task.series(
copyCodiconsTask,
cleanExtensionsBuildTask,
compileNonNativeExtensionsBuildTask,
compileExtensionMediaBuildTask,
writeISODate('out-build'),
@@ -269,10 +271,7 @@ const coreCIEsbuild = task.define('core-ci-esbuild', task.series(
task.define('esbuild-vscode-reh-min', () => runEsbuildBundle('out-vscode-reh-min', true, true, 'server', `${sourceMappingURLBase}/core`)),
task.define('esbuild-vscode-reh-web-min', () => runEsbuildBundle('out-vscode-reh-web-min', true, true, 'server-web', `${sourceMappingURLBase}/core`)),
)
));
gulp.task(coreCIEsbuild);
gulp.task(task.define('core-ci', useEsbuildTranspile ? coreCIEsbuild : coreCIOld));
)));
const coreCIPR = task.define('core-ci-pr', task.series(
gulp.task('compile-build-without-mangling') as task.Task,
@@ -353,8 +352,11 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
const extensions = gulp.src(['.build/extensions/**', ...platformSpecificBuiltInExtensionsExclusions], { base: '.build', dot: true });
const sourceFilterPattern = stripSourceMapsInPackagingTasks
? ['**', '!**/*.{js,css}.map']
: ['**'];
const sources = es.merge(src, extensions)
.pipe(filter(['**', '!**/*.{js,css}.map'], { dot: true }));
.pipe(filter(sourceFilterPattern, { dot: true }));
let version = packageJson.version;
const quality = (product as { quality?: string }).quality;
@@ -389,7 +391,7 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
const isInsiderOrExploration = quality === 'insider' || quality === 'exploration';
const embedded = isInsiderOrExploration
? (product as typeof product & { embedded?: { nameShort: string; nameLong: string; applicationName: string; dataFolderName: string; darwinBundleIdentifier: string; urlProtocol: string } }).embedded
? (product as typeof product & { embedded?: EmbeddedProductInfo }).embedded
: undefined;
const packageSubJsonStream = isInsiderOrExploration
@@ -404,12 +406,9 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
const productSubJsonStream = embedded
? gulp.src(['product.json'], { base: '.' })
.pipe(jsonEditor((json: Record<string, unknown>) => {
json.nameShort = embedded.nameShort;
json.nameLong = embedded.nameLong;
json.applicationName = embedded.applicationName;
json.dataFolderName = embedded.dataFolderName;
json.darwinBundleIdentifier = embedded.darwinBundleIdentifier;
json.urlProtocol = embedded.urlProtocol;
Object.keys(embedded).forEach(key => {
json[key] = embedded[key as keyof EmbeddedProductInfo];
});
return json;
}))
.pipe(rename('product.sub.json'))
@@ -427,8 +426,13 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
const productionDependencies = getProductionDependencies(root);
const dependenciesSrc = productionDependencies.map(d => path.relative(root, d)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`]).flat().concat('!**/*.mk');
const depFilterPattern = ['**', `!**/${config.version}/**`, '!**/bin/darwin-arm64-87/**', '!**/package-lock.json', '!**/yarn.lock'];
if (stripSourceMapsInPackagingTasks) {
depFilterPattern.push('!**/*.{js,css}.map');
}
const deps = gulp.src(dependenciesSrc, { base: '.', dot: true })
.pipe(filter(['**', `!**/${config.version}/**`, '!**/bin/darwin-arm64-87/**', '!**/package-lock.json', '!**/yarn.lock', '!**/*.{js,css}.map']))
.pipe(filter(depFilterPattern))
.pipe(util.cleanNodeModules(path.join(import.meta.dirname, '.moduleignore')))
.pipe(util.cleanNodeModules(path.join(import.meta.dirname, `.moduleignore.${process.platform}`)))
.pipe(jsFilter)
@@ -500,6 +504,9 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
'resources/win32/code_70x70.png',
'resources/win32/code_150x150.png'
], { base: '.' }));
if (embedded) {
all = es.merge(all, gulp.src('resources/win32/sessions.ico', { base: '.' }));
}
} else if (platform === 'linux') {
const policyDest = gulp.src('.build/policies/linux/**', { base: '.build/policies/linux' })
.pipe(rename(f => f.dirname = `policies/${f.dirname}`));
@@ -523,6 +530,13 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
darwinMiniAppName: embedded.nameShort,
darwinMiniAppBundleIdentifier: embedded.darwinBundleIdentifier,
darwinMiniAppIcon: 'resources/darwin/sessions.icns',
darwinMiniAppBundleURLTypes: [{
role: 'Viewer',
name: embedded.nameLong,
urlSchemes: [embedded.urlProtocol]
}],
win32ProxyAppName: embedded.nameShort,
win32ProxyIcon: 'resources/win32/sessions.ico',
} : {})
};
@@ -531,7 +545,13 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
.pipe(util.fixWin32DirectoryPermissions())
.pipe(filter(['**', '!**/.github/**'], { dot: true })) // https://github.com/microsoft/vscode/issues/116523
.pipe(electron(electronConfig))
.pipe(filter(['**', '!LICENSE', '!version'], { dot: true }));
.pipe(filter([
'**',
'!LICENSE',
'!version',
...(platform === 'darwin' && !isInsiderOrExploration ? ['!**/Contents/Applications'] : []),
...(platform === 'win32' && !isInsiderOrExploration ? ['!**/electron_proxy.exe'] : []),
], { dot: true }));
if (platform === 'linux') {
result = es.merge(result, gulp.src('resources/completions/bash/code', { base: '.' })
@@ -579,6 +599,7 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
}
result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' })
.pipe(replace('@@VERSIONFOLDER@@', versionedResourcesFolder ? `${versionedResourcesFolder}\\` : ''))
.pipe(rename(product.nameShort + '.VisualElementsManifest.xml')));
result = es.merge(result, gulp.src('.build/policies/win32/**', { base: '.build/policies/win32' })
@@ -692,7 +713,13 @@ BUILD_TARGETS.forEach(buildTarget => {
if (useEsbuildTranspile) {
const esbuildBundleTask = task.define(
`esbuild-bundle${dashed(platform)}${dashed(arch)}${dashed(minified)}`,
() => runEsbuildBundle(sourceFolderName, !!minified, true, 'desktop', minified ? `${sourceMappingURLBase}/core` : undefined)
() => runEsbuildBundle(
sourceFolderName,
!!minified,
true,
'desktop',
minified && useCdnSourceMapsForPackagingTasks ? `${sourceMappingURLBase}/core` : undefined
)
);
vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
copyCodiconsTask,
+6 -2
View File
@@ -33,7 +33,7 @@ const quality = (product as { quality?: string }).quality;
const version = (quality && quality !== 'stable') ? `${packageJson.version}-${quality}` : packageJson.version;
// esbuild-based bundle for standalone web
function runEsbuildBundle(outDir: string, minify: boolean, nls: boolean): Promise<void> {
function runEsbuildBundle(outDir: string, minify: boolean, nls: boolean, sourceMapBaseUrl?: string): Promise<void> {
return new Promise((resolve, reject) => {
const scriptPath = path.join(REPO_ROOT, 'build/next/index.ts');
const args = [scriptPath, 'bundle', '--out', outDir, '--target', 'web'];
@@ -44,6 +44,9 @@ function runEsbuildBundle(outDir: string, minify: boolean, nls: boolean): Promis
if (nls) {
args.push('--nls');
}
if (sourceMapBaseUrl) {
args.push('--source-map-base-url', sourceMapBaseUrl);
}
const proc = cp.spawn(process.execPath, args, {
cwd: REPO_ROOT,
@@ -164,8 +167,9 @@ const minifyVSCodeWebTask = task.define('minify-vscode-web-OLD', task.series(
gulp.task(minifyVSCodeWebTask);
// esbuild-based tasks (new)
const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;
const esbuildBundleVSCodeWebTask = task.define('esbuild-vscode-web', () => runEsbuildBundle('out-vscode-web', false, true));
const esbuildBundleVSCodeWebMinTask = task.define('esbuild-vscode-web-min', () => runEsbuildBundle('out-vscode-web-min', true, true));
const esbuildBundleVSCodeWebMinTask = task.define('esbuild-vscode-web-min', () => runEsbuildBundle('out-vscode-web-min', true, true, `${sourceMappingURLBase}/core`));
function packageTask(sourceFolderName: string, destinationFolderName: string) {
const destination = path.join(BUILD_ROOT, destinationFolderName);
+12
View File
@@ -14,6 +14,7 @@ import product from '../product.json' with { type: 'json' };
import { getVersion } from './lib/getVersion.ts';
import * as task from './lib/task.ts';
import * as util from './lib/util.ts';
import type { EmbeddedProductInfo } from './lib/embeddedType.ts';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
@@ -112,6 +113,17 @@ function buildWin32Setup(arch: string, target: string): task.CallbackTask {
Quality: quality
};
const isInsiderOrExploration = false;
const embedded = isInsiderOrExploration
? (product as typeof product & { embedded?: EmbeddedProductInfo }).embedded
: undefined;
if (embedded) {
definitions['ProxyExeBasename'] = embedded.nameShort;
definitions['ProxyAppUserId'] = embedded.win32AppUserModelId;
definitions['ProxyNameLong'] = embedded.nameLong;
}
if (quality === 'stable' || quality === 'insider') {
definitions['AppxPackage'] = `${quality === 'stable' ? 'code' : 'code_insider'}_${arch}.appx`;
definitions['AppxPackageDll'] = `${quality === 'stable' ? 'code' : 'code_insider'}_explorer_command_${arch}.dll`;
+15 -1
View File
@@ -5,9 +5,23 @@
import path from 'path';
import fs from 'fs';
import { execSync } from 'child_process';
const root = path.join(import.meta.dirname, '..', '..');
/**
* Get the ISO date for the build. Uses the git commit date of HEAD
* so that independent builds on different machines produce the same
* timestamp (required for deterministic builds, e.g. macOS Universal).
*/
export function getGitCommitDate(): string {
try {
return execSync('git log -1 --format=%cI HEAD', { cwd: root, encoding: 'utf8' }).trim();
} catch {
return new Date().toISOString();
}
}
/**
* Writes a `outDir/date` file with the contents of the build
* so that other tasks during the build process can use it and
@@ -18,7 +32,7 @@ export function writeISODate(outDir: string) {
const outDirectory = path.join(root, outDir);
fs.mkdirSync(outDirectory, { recursive: true });
const date = new Date().toISOString();
const date = getGitCommitDate();
fs.writeFileSync(path.join(outDirectory, 'date'), date, 'utf8');
resolve();
@@ -2,12 +2,17 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import withDefaults from '../shared.webpack.config.mjs';
export default withDefaults({
context: import.meta.dirname,
entry: {
extension: './src/extension.ts',
},
});
export type EmbeddedProductInfo = {
nameShort: string;
nameLong: string;
applicationName: string;
dataFolderName: string;
darwinBundleIdentifier: string;
urlProtocol: string;
win32AppUserModelId: string;
win32MutexName: string;
win32RegValueName: string;
win32NameVersion: string;
win32VersionedUpdate: boolean;
};
+4 -204
View File
@@ -20,10 +20,8 @@ import fancyLog from 'fancy-log';
import ansiColors from 'ansi-colors';
import buffer from 'gulp-buffer';
import * as jsoncParser from 'jsonc-parser';
import webpack from 'webpack';
import { getProductionDependencies } from './dependencies.ts';
import { type IExtensionDefinition, getExtensionStream } from './builtInExtensions.ts';
import { getVersion } from './getVersion.ts';
import { fetchUrls, fetchGithub } from './fetch.ts';
import { createTsgoStream, spawnTsgo } from './tsgo.ts';
import vzip from 'gulp-vinyl-zip';
@@ -32,8 +30,8 @@ import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const root = path.dirname(path.dirname(import.meta.dirname));
const commit = getVersion(root);
const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;
// const commit = getVersion(root);
// const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;
function minifyExtensionResources(input: Stream): Stream {
const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true });
@@ -65,32 +63,24 @@ function updateExtensionPackageJSON(input: Stream, update: (data: any) => any):
.pipe(packageJsonFilter.restore);
}
function fromLocal(extensionPath: string, forWeb: boolean, disableMangle: boolean): Stream {
function fromLocal(extensionPath: string, forWeb: boolean, _disableMangle: boolean): Stream {
const esbuildConfigFileName = forWeb
? 'esbuild.browser.mts'
: 'esbuild.mts';
const webpackConfigFileName = forWeb
? `extension-browser.webpack.config.js`
: `extension.webpack.config.js`;
const hasEsbuild = fs.existsSync(path.join(extensionPath, esbuildConfigFileName));
const hasWebpack = fs.existsSync(path.join(extensionPath, webpackConfigFileName));
let input: Stream;
let isBundled = false;
if (hasEsbuild) {
// Unlike webpack, esbuild only does bundling so we still want to run a separate type check step
// Esbuild only does bundling so we still want to run a separate type check step
input = es.merge(
fromLocalEsbuild(extensionPath, esbuildConfigFileName),
...getBuildRootsForExtension(extensionPath).map(root => typeCheckExtensionStream(root, forWeb)),
);
isBundled = true;
} else if (hasWebpack) {
input = fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle);
isBundled = true;
} else {
input = fromLocalNormal(extensionPath);
}
@@ -122,132 +112,6 @@ export function typeCheckExtensionStream(extensionPath: string, forWeb: boolean)
return createTsgoStream(tsconfigPath, { taskName: 'typechecking extension (tsgo)', noEmit: true });
}
function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string, disableMangle: boolean): Stream {
const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
const result = es.through();
const packagedDependencies: string[] = [];
const stripOutSourceMaps: string[] = [];
const packageJsonConfig = require(path.join(extensionPath, 'package.json'));
if (packageJsonConfig.dependencies) {
const webpackConfig = require(path.join(extensionPath, webpackConfigFileName));
const webpackRootConfig = webpackConfig.default;
for (const key in webpackRootConfig.externals) {
if (key in packageJsonConfig.dependencies) {
packagedDependencies.push(key);
}
}
if (webpackConfig.StripOutSourceMaps) {
for (const filePath of webpackConfig.StripOutSourceMaps) {
stripOutSourceMaps.push(filePath);
}
}
}
// TODO: add prune support based on packagedDependencies to vsce.PackageManager.Npm similar
// to vsce.PackageManager.Yarn.
// A static analysis showed there are no webpack externals that are dependencies of the current
// local extensions so we can use the vsce.PackageManager.None config to ignore dependencies list
// as a temporary workaround.
vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.None, packagedDependencies }).then(fileNames => {
const files = fileNames
.map(fileName => path.join(extensionPath, fileName))
.map(filePath => new File({
path: filePath,
stat: fs.statSync(filePath),
base: extensionPath,
contents: fs.createReadStream(filePath)
}));
// check for a webpack configuration files, then invoke webpack
// and merge its output with the files stream.
const webpackConfigLocations = (glob.sync(
path.join(extensionPath, '**', webpackConfigFileName),
{ ignore: ['**/node_modules'] }
) as string[]);
const webpackStreams = webpackConfigLocations.flatMap(webpackConfigPath => {
const webpackDone = (err: Error | undefined, stats: any) => {
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`);
if (err) {
result.emit('error', err);
}
const { compilation } = stats;
if (compilation.errors.length > 0) {
result.emit('error', compilation.errors.join('\n'));
}
if (compilation.warnings.length > 0) {
result.emit('error', compilation.warnings.join('\n'));
}
};
const exportedConfig = require(webpackConfigPath).default;
return (Array.isArray(exportedConfig) ? exportedConfig : [exportedConfig]).map(config => {
const webpackConfig = {
...config,
...{ mode: 'production' }
};
if (disableMangle) {
if (Array.isArray(config.module.rules)) {
for (const rule of config.module.rules) {
if (Array.isArray(rule.use)) {
for (const use of rule.use) {
if (String(use.loader).endsWith('mangle-loader.js')) {
use.options.disabled = true;
}
}
}
}
}
}
const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path);
return webpackGulp(webpackConfig, webpack, webpackDone)
.pipe(es.through(function (data) {
data.stat = data.stat || {};
data.base = extensionPath;
this.emit('data', data);
}))
.pipe(es.through(function (data: File) {
// source map handling:
// * rewrite sourceMappingURL
// * save to disk so that upload-task picks this up
if (path.extname(data.basename) === '.js') {
if (stripOutSourceMaps.indexOf(data.relative) >= 0) { // remove source map
const contents = (data.contents as Buffer).toString('utf8');
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8');
} else {
const contents = (data.contents as Buffer).toString('utf8');
data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) {
return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`;
}), 'utf8');
}
}
this.emit('data', data);
}));
});
});
es.merge(...webpackStreams, es.readArray(files))
// .pipe(es.through(function (data) {
// // debug
// console.log('out', data.path, data.contents.length);
// this.emit('data', data);
// }))
.pipe(result);
}).catch(err => {
console.error(extensionPath);
console.error(packagedDependencies);
result.emit('error', err);
});
return result.pipe(createStatsStream(path.basename(extensionPath)));
}
function fromLocalNormal(extensionPath: string): Stream {
const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
@@ -649,70 +513,6 @@ export function translatePackageJSON(packageJSON: string, packageNLSPath: string
const extensionsPath = path.join(root, 'extensions');
export async function webpackExtensions(taskName: string, isWatch: boolean, webpackConfigLocations: { configPath: string; outputRoot?: string }[]) {
const webpack = require('webpack') as typeof import('webpack');
const webpackConfigs: webpack.Configuration[] = [];
for (const { configPath, outputRoot } of webpackConfigLocations) {
const configOrFnOrArray = require(configPath).default;
function addConfig(configOrFnOrArray: webpack.Configuration | ((env: unknown, args: unknown) => webpack.Configuration) | webpack.Configuration[]) {
for (const configOrFn of Array.isArray(configOrFnOrArray) ? configOrFnOrArray : [configOrFnOrArray]) {
const config = typeof configOrFn === 'function' ? configOrFn({}, {}) : configOrFn;
if (outputRoot) {
config.output!.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output!.path!));
}
webpackConfigs.push(config);
}
}
addConfig(configOrFnOrArray);
}
function reporter(fullStats: any) {
if (Array.isArray(fullStats.children)) {
for (const stats of fullStats.children) {
const outputPath = stats.outputPath;
if (outputPath) {
const relativePath = path.relative(extensionsPath, outputPath).replace(/\\/g, '/');
const match = relativePath.match(/[^\/]+(\/server|\/client)?/);
fancyLog(`Finished ${ansiColors.green(taskName)} ${ansiColors.cyan(match![0])} with ${stats.errors.length} errors.`);
}
if (Array.isArray(stats.errors)) {
stats.errors.forEach((error: any) => {
fancyLog.error(error);
});
}
if (Array.isArray(stats.warnings)) {
stats.warnings.forEach((warning: any) => {
fancyLog.warn(warning);
});
}
}
}
}
return new Promise<void>((resolve, reject) => {
if (isWatch) {
webpack(webpackConfigs).watch({}, (err, stats) => {
if (err) {
reject();
} else {
reporter(stats?.toJson());
}
});
} else {
webpack(webpackConfigs).run((err, stats) => {
if (err) {
fancyLog.error(err);
reject();
} else {
reporter(stats?.toJson());
resolve();
}
});
}
});
}
export async function esbuildExtensions(taskName: string, isWatch: boolean, scripts: { script: string; outputRoot?: string }[]): Promise<void> {
function reporter(stdError: string, script: string) {
const matches = (stdError || '').match(/\> (.+): error: (.+)?/g);
+28 -14
View File
@@ -38,20 +38,6 @@
}
],
"policies": [
{
"key": "chat.mcp.gallery.serviceUrl",
"name": "McpGalleryServiceUrl",
"category": "InteractiveSession",
"minimumVersion": "1.101",
"localization": {
"description": {
"key": "mcp.gallery.serviceUrl",
"value": "Configure the MCP Gallery service URL to connect to"
}
},
"type": "string",
"default": ""
},
{
"key": "extensions.gallery.serviceUrl",
"name": "ExtensionGalleryServiceUrl",
@@ -66,6 +52,20 @@
"type": "string",
"default": ""
},
{
"key": "chat.mcp.gallery.serviceUrl",
"name": "McpGalleryServiceUrl",
"category": "InteractiveSession",
"minimumVersion": "1.101",
"localization": {
"description": {
"key": "mcp.gallery.serviceUrl",
"value": "Configure the MCP Gallery service URL to connect to"
}
},
"type": "string",
"default": ""
},
{
"key": "extensions.allowed",
"name": "AllowedExtensions",
@@ -286,6 +286,20 @@
},
"type": "boolean",
"default": true
},
{
"key": "workbench.browser.enableChatTools",
"name": "BrowserChatTools",
"category": "InteractiveSession",
"minimumVersion": "1.110",
"localization": {
"description": {
"key": "browser.enableChatTools",
"value": "When enabled, chat agents can use browser tools to open and interact with pages in the Integrated Browser."
}
},
"type": "boolean",
"default": false
}
]
}
@@ -21,6 +21,7 @@
"--vscode-activityErrorBadge-foreground",
"--vscode-activityWarningBadge-background",
"--vscode-activityWarningBadge-foreground",
"--vscode-agentFeedbackInputWidget-border",
"--vscode-agentSessionReadIndicator-foreground",
"--vscode-agentSessionSelectedBadge-border",
"--vscode-agentSessionSelectedUnfocusedBadge-border",
@@ -35,6 +36,7 @@
"--vscode-breadcrumb-focusForeground",
"--vscode-breadcrumb-foreground",
"--vscode-breadcrumbPicker-background",
"--vscode-browser-border",
"--vscode-button-background",
"--vscode-button-border",
"--vscode-button-foreground",
@@ -644,6 +646,8 @@
"--vscode-searchEditor-findMatchBorder",
"--vscode-searchEditor-textInputBorder",
"--vscode-selection-background",
"--vscode-sessionsUpdateButton-downloadedBackground",
"--vscode-sessionsUpdateButton-downloadingBackground",
"--vscode-settings-checkboxBackground",
"--vscode-settings-checkboxBorder",
"--vscode-settings-checkboxForeground",
@@ -968,6 +972,14 @@
"--vscode-repl-line-height",
"--vscode-sash-hover-size",
"--vscode-sash-size",
"--vscode-shadow-active-tab",
"--vscode-shadow-depth-x",
"--vscode-shadow-depth-y",
"--vscode-shadow-hover",
"--vscode-shadow-lg",
"--vscode-shadow-md",
"--vscode-shadow-sm",
"--vscode-shadow-xl",
"--vscode-testing-coverage-lineHeight",
"--vscode-editorStickyScroll-scrollableWidth",
"--vscode-editorStickyScroll-foldingOpacityTransition",
@@ -998,6 +1010,7 @@
"--text-link-decoration",
"--vscode-action-item-auto-timeout",
"--monaco-editor-warning-decoration",
"--animation-angle",
"--animation-opacity",
"--chat-setup-dialog-glow-angle",
"--vscode-chat-font-family",
+54 -17
View File
@@ -7,11 +7,13 @@ import * as esbuild from 'esbuild';
import * as fs from 'fs';
import * as path from 'path';
import { promisify } from 'util';
import glob from 'glob';
import gulpWatch from '../lib/watch/index.ts';
import { nlsPlugin, createNLSCollector, finalizeNLS, postProcessNLS } from './nls-plugin.ts';
import { convertPrivateFields, adjustSourceMap, type ConvertPrivateFieldsResult } from './private-to-property.ts';
import { getVersion } from '../lib/getVersion.ts';
import { getGitCommitDate } from '../lib/date.ts';
import product from '../../product.json' with { type: 'json' };
import packageJson from '../../package.json' with { type: 'json' };
import { useEsbuildTranspile } from '../buildConfig.ts';
@@ -72,7 +74,8 @@ const extensionHostEntryPoints = [
];
function isExtensionHostBundle(filePath: string): boolean {
return extensionHostEntryPoints.some(ep => filePath.endsWith(`${ep}.js`));
const normalized = filePath.replaceAll('\\', '/');
return extensionHostEntryPoints.some(ep => normalized.endsWith(`${ep}.js`));
}
// Workers - shared between targets
@@ -419,13 +422,13 @@ function scanBuiltinExtensions(extensionsRoot: string): Array<IScannedBuiltinExt
}
/**
* Get the date from the out directory date file, or return current date.
* Get the date from the out directory date file, or return the git commit date.
*/
function readISODate(outDir: string): string {
try {
return fs.readFileSync(path.join(REPO_ROOT, outDir, 'date'), 'utf8');
} catch {
return new Date().toISOString();
return getGitCommitDate();
}
}
@@ -723,10 +726,19 @@ async function transpile(outDir: string, excludeTests: boolean): Promise<void> {
async function bundle(outDir: string, doMinify: boolean, doNls: boolean, doManglePrivates: boolean, target: BuildTarget, sourceMapBaseUrl?: string): Promise<void> {
await cleanDir(outDir);
// Write build date file (used by packaging to embed in product.json)
// Write build date file (used by packaging to embed in product.json).
// Reuse the date from out-build/date if it exists (written by the gulp
// writeISODate task) so that all parallel bundle outputs share the same
// timestamp - this is required for deterministic builds (e.g. macOS Universal).
const outDirPath = path.join(REPO_ROOT, outDir);
await fs.promises.mkdir(outDirPath, { recursive: true });
await fs.promises.writeFile(path.join(outDirPath, 'date'), new Date().toISOString(), 'utf8');
let buildDate: string;
try {
buildDate = await fs.promises.readFile(path.join(REPO_ROOT, 'out-build', 'date'), 'utf8');
} catch {
buildDate = getGitCommitDate();
}
await fs.promises.writeFile(path.join(outDirPath, 'date'), buildDate, 'utf8');
console.log(`[bundle] ${SRC_DIR}${outDir} (target: ${target})${doMinify ? ' (minify)' : ''}${doNls ? ' (nls)' : ''}${doManglePrivates ? ' (mangle-privates)' : ''}`);
const t1 = Date.now();
@@ -885,6 +897,13 @@ ${tslib}`,
const mangleStats: { file: string; result: ConvertPrivateFieldsResult }[] = [];
// Map from JS file path to pre-mangle content + edits, for source map adjustment
const mangleEdits = new Map<string, { preMangleCode: string; edits: readonly import('./private-to-property.ts').TextEdit[] }>();
// Map from JS file path to pre-NLS content + edits, for source map adjustment
const nlsEdits = new Map<string, { preNLSCode: string; edits: readonly import('./private-to-property.ts').TextEdit[] }>();
// Defer .map files until all .js files are processed, because esbuild may
// emit the .map file in a different build result than the .js file (e.g.
// code-split chunks), and we need the NLS/mangle edits from the .js pass
// to be available when adjusting the .map.
const deferredMaps: { path: string; text: string; contents: Uint8Array }[] = [];
for (const { result } of buildResults) {
if (!result.outputFiles) {
continue;
@@ -913,7 +932,12 @@ ${tslib}`,
// Apply NLS post-processing if enabled (JS only)
if (file.path.endsWith('.js') && doNls && indexMap.size > 0) {
content = postProcessNLS(content, indexMap, preserveEnglish);
const preNLSCode = content;
const nlsResult = postProcessNLS(content, indexMap, preserveEnglish);
content = nlsResult.code;
if (nlsResult.edits.length > 0) {
nlsEdits.set(file.path, { preNLSCode, edits: nlsResult.edits });
}
}
// Rewrite sourceMappingURL to CDN URL if configured
@@ -931,16 +955,8 @@ ${tslib}`,
await fs.promises.writeFile(file.path, content);
} else if (file.path.endsWith('.map')) {
// Source maps may need adjustment if private fields were mangled
const jsPath = file.path.replace(/\.map$/, '');
const editInfo = mangleEdits.get(jsPath);
if (editInfo) {
const mapJson = JSON.parse(file.text);
const adjusted = adjustSourceMap(mapJson, editInfo.preMangleCode, editInfo.edits);
await fs.promises.writeFile(file.path, JSON.stringify(adjusted));
} else {
await fs.promises.writeFile(file.path, file.contents);
}
// Defer .map processing until all .js files have been handled
deferredMaps.push({ path: file.path, text: file.text, contents: file.contents });
} else {
// Write other files (assets, etc.) as-is
await fs.promises.writeFile(file.path, file.contents);
@@ -949,6 +965,27 @@ ${tslib}`,
bundled++;
}
// Second pass: process deferred .map files now that all mangle/NLS edits
// have been collected from .js processing above.
for (const mapFile of deferredMaps) {
const jsPath = mapFile.path.replace(/\.map$/, '');
const mangle = mangleEdits.get(jsPath);
const nls = nlsEdits.get(jsPath);
if (mangle || nls) {
let mapJson = JSON.parse(mapFile.text);
if (mangle) {
mapJson = adjustSourceMap(mapJson, mangle.preMangleCode, mangle.edits);
}
if (nls) {
mapJson = adjustSourceMap(mapJson, nls.preNLSCode, nls.edits);
}
await fs.promises.writeFile(mapFile.path, JSON.stringify(mapJson));
} else {
await fs.promises.writeFile(mapFile.path, mapFile.contents);
}
}
// Log mangle-privates stats
if (doManglePrivates && mangleStats.length > 0) {
let totalClasses = 0, totalFields = 0, totalEdits = 0, totalElapsed = 0;
@@ -1128,7 +1165,7 @@ async function main(): Promise<void> {
// Write build date file (used by packaging to embed in product.json)
const outDirPath = path.join(REPO_ROOT, outDir);
await fs.promises.mkdir(outDirPath, { recursive: true });
await fs.promises.writeFile(path.join(outDirPath, 'date'), new Date().toISOString(), 'utf8');
await fs.promises.writeFile(path.join(outDirPath, 'date'), getGitCommitDate(), 'utf8');
console.log(`[transpile] ${SRC_DIR}${outDir}${options.excludeTests ? ' (excluding tests)' : ''}`);
const t1 = Date.now();
+80 -52
View File
@@ -12,6 +12,7 @@ import {
analyzeLocalizeCalls,
parseLocalizeKeyOrValue
} from '../lib/nls-analysis.ts';
import type { TextEdit } from './private-to-property.ts';
// ============================================================================
// Types
@@ -148,12 +149,13 @@ export async function finalizeNLS(
/**
* Post-processes a JavaScript file to replace NLS placeholders with indices.
* Returns the transformed code and the edits applied (for source map adjustment).
*/
export function postProcessNLS(
content: string,
indexMap: Map<string, number>,
preserveEnglish: boolean
): string {
): { code: string; edits: readonly TextEdit[] } {
return replaceInOutput(content, indexMap, preserveEnglish);
}
@@ -244,7 +246,7 @@ function generateNLSSourceMap(
const generator = new SourceMapGenerator();
generator.setSourceContent(filePath, originalSource);
const lineCount = originalSource.split('\n').length;
const lines = originalSource.split('\n');
// Group edits by line
const editsByLine = new Map<number, NLSEdit[]>();
@@ -257,7 +259,7 @@ function generateNLSSourceMap(
arr.push(edit);
}
for (let line = 0; line < lineCount; line++) {
for (let line = 0; line < lines.length; line++) {
const smLine = line + 1; // source maps use 1-based lines
// Always map start of line
@@ -273,7 +275,8 @@ function generateNLSSourceMap(
let cumulativeShift = 0;
for (const edit of lineEdits) {
for (let i = 0; i < lineEdits.length; i++) {
const edit = lineEdits[i];
const origLen = edit.endCol - edit.startCol;
// Map start of edit: the replacement begins at the same original position
@@ -285,12 +288,20 @@ function generateNLSSourceMap(
cumulativeShift += edit.newLength - origLen;
// Map content after edit: columns resume with the shift applied
generator.addMapping({
generated: { line: smLine, column: edit.endCol + cumulativeShift },
original: { line: smLine, column: edit.endCol },
source: filePath,
});
// Source maps don't interpolate columns — each query resolves to the
// last segment with generatedColumn <= queryColumn. A single mapping
// at edit-end would cause every subsequent column on this line to
// collapse to that one original position. Add per-column identity
// mappings from edit-end to the next edit (or end of line) so that
// esbuild's source-map composition preserves fine-grained accuracy.
const nextBound = i + 1 < lineEdits.length ? lineEdits[i + 1].startCol : lines[line].length;
for (let origCol = edit.endCol; origCol < nextBound; origCol++) {
generator.addMapping({
generated: { line: smLine, column: origCol + cumulativeShift },
original: { line: smLine, column: origCol },
source: filePath,
});
}
}
}
}
@@ -302,17 +313,19 @@ function replaceInOutput(
content: string,
indexMap: Map<string, number>,
preserveEnglish: boolean
): string {
// Replace all placeholders in a single pass using regex
// Two types of placeholders:
// - %%NLS:moduleId#key%% for localize() - message replaced with null
// - %%NLS2:moduleId#key%% for localize2() - message preserved
// Note: esbuild may use single or double quotes, so we handle both
): { code: string; edits: readonly TextEdit[] } {
// Collect all matches first, then apply from back to front so that byte
// offsets remain valid. Each match becomes a TextEdit in terms of the
// ORIGINAL content offsets, which is what adjustSourceMap expects.
interface PendingEdit { start: number; end: number; replacement: string }
const pending: PendingEdit[] = [];
if (preserveEnglish) {
// Just replace the placeholder with the index (both NLS and NLS2)
return content.replace(/["']%%NLS2?:([^%]+)%%["']/g, (match, inner) => {
// Try NLS first, then NLS2
const re = /["']%%NLS2?:([^%]+)%%["']/g;
let m: RegExpExecArray | null;
while ((m = re.exec(content)) !== null) {
const inner = m[1];
let placeholder = `%%NLS:${inner}%%`;
let index = indexMap.get(placeholder);
if (index === undefined) {
@@ -320,45 +333,60 @@ function replaceInOutput(
index = indexMap.get(placeholder);
}
if (index !== undefined) {
return String(index);
pending.push({ start: m.index, end: m.index + m[0].length, replacement: String(index) });
}
// Placeholder not found in map, leave as-is (shouldn't happen)
return match;
});
}
} else {
// For NLS (localize): replace placeholder with index AND replace message with null
// For NLS2 (localize2): replace placeholder with index, keep message
// Note: Use (?:[^"\\]|\\.)* to properly handle escaped quotes like \" or \\
// Note: esbuild may use single or double quotes, so we handle both
// First handle NLS (localize) - replace both key and message
content = content.replace(
/["']%%NLS:([^%]+)%%["'](\s*,\s*)(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)/g,
(match, inner, comma) => {
const placeholder = `%%NLS:${inner}%%`;
const index = indexMap.get(placeholder);
if (index !== undefined) {
return `${index}${comma}null`;
}
return match;
// NLS (localize): replace placeholder with index AND replace message with null
const reNLS = /["']%%NLS:([^%]+)%%["'](\s*,\s*)(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)/g;
let m: RegExpExecArray | null;
while ((m = reNLS.exec(content)) !== null) {
const inner = m[1];
const comma = m[2];
const placeholder = `%%NLS:${inner}%%`;
const index = indexMap.get(placeholder);
if (index !== undefined) {
pending.push({ start: m.index, end: m.index + m[0].length, replacement: `${index}${comma}null` });
}
);
}
// Then handle NLS2 (localize2) - replace only key, keep message
content = content.replace(
/["']%%NLS2:([^%]+)%%["']/g,
(match, inner) => {
const placeholder = `%%NLS2:${inner}%%`;
const index = indexMap.get(placeholder);
if (index !== undefined) {
return String(index);
}
return match;
// NLS2 (localize2): replace only key, keep message
const reNLS2 = /["']%%NLS2:([^%]+)%%["']/g;
while ((m = reNLS2.exec(content)) !== null) {
const inner = m[1];
const placeholder = `%%NLS2:${inner}%%`;
const index = indexMap.get(placeholder);
if (index !== undefined) {
pending.push({ start: m.index, end: m.index + m[0].length, replacement: String(index) });
}
);
return content;
}
}
if (pending.length === 0) {
return { code: content, edits: [] };
}
// Sort by offset ascending, then apply back-to-front to keep offsets valid
pending.sort((a, b) => a.start - b.start);
// Build TextEdit[] (in original-content coordinates) and apply edits
const edits: TextEdit[] = [];
for (const p of pending) {
edits.push({ start: p.start, end: p.end, newText: p.replacement });
}
// Apply edits using forward-scanning parts array — O(N+K) instead of
// O(N*K) from repeated substring concatenation on large strings.
const parts: string[] = [];
let lastEnd = 0;
for (const p of pending) {
parts.push(content.substring(lastEnd, p.start));
parts.push(p.replacement);
lastEnd = p.end;
}
parts.push(content.substring(lastEnd));
return { code: parts.join(''), edits };
}
// ============================================================================
+124 -39
View File
@@ -220,15 +220,53 @@ export function adjustSourceMap(
return sourceMapJson;
}
// Build a line-offset table for the original code to convert byte offsets to line/column
const lineStarts: number[] = [0];
for (let i = 0; i < originalCode.length; i++) {
if (originalCode.charCodeAt(i) === 10 /* \n */) {
lineStarts.push(i + 1);
}
// Build line-offset tables for the original code and the code after edits.
// When edits span newlines (e.g. NLS replacing a multi-line template literal
// with `null`), subsequent lines shift up and columns change. We handle this
// by converting each mapping's old generated (line, col) to a byte offset,
// adjusting the offset for the edits, then converting back to (line, col) in
// the post-edit coordinate system.
const oldLineStarts = buildLineStarts(originalCode);
const newLineStarts = buildLineStartsAfterEdits(originalCode, edits);
// Precompute cumulative byte-shift after each edit for binary search
const n = edits.length;
const editStarts: number[] = new Array(n);
const editEnds: number[] = new Array(n);
const cumShifts: number[] = new Array(n); // cumulative shift *after* edit[i]
let cumShift = 0;
for (let i = 0; i < n; i++) {
editStarts[i] = edits[i].start;
editEnds[i] = edits[i].end;
cumShift += edits[i].newText.length - (edits[i].end - edits[i].start);
cumShifts[i] = cumShift;
}
function offsetToLineCol(offset: number): { line: number; col: number } {
function adjustOffset(oldOff: number): number {
// Binary search: find last edit with start <= oldOff
let lo = 0, hi = n - 1;
while (lo <= hi) {
const mid = (lo + hi) >> 1;
if (editStarts[mid] <= oldOff) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
// hi = index of last edit where start <= oldOff, or -1 if none
if (hi < 0) {
return oldOff;
}
if (oldOff < editEnds[hi]) {
// Inside edit range — clamp to edit start in new coordinates
const prevShift = hi > 0 ? cumShifts[hi - 1] : 0;
return editStarts[hi] + prevShift;
}
return oldOff + cumShifts[hi];
}
function offsetToLineCol(lineStarts: readonly number[], offset: number): { line: number; col: number } {
let lo = 0, hi = lineStarts.length - 1;
while (lo < hi) {
const mid = (lo + hi + 1) >> 1;
@@ -241,23 +279,9 @@ export function adjustSourceMap(
return { line: lo, col: offset - lineStarts[lo] };
}
// Convert edits from byte offsets to per-line column shifts
interface LineEdit { col: number; origLen: number; newLen: number }
const editsByLine = new Map<number, LineEdit[]>();
for (const edit of edits) {
const pos = offsetToLineCol(edit.start);
const origLen = edit.end - edit.start;
let arr = editsByLine.get(pos.line);
if (!arr) {
arr = [];
editsByLine.set(pos.line, arr);
}
arr.push({ col: pos.col, origLen, newLen: edit.newText.length });
}
// Use source-map library to read, adjust, and write
const consumer = new SourceMapConsumer(sourceMapJson);
const generator = new SourceMapGenerator({ file: sourceMapJson.file });
const generator = new SourceMapGenerator({ file: sourceMapJson.file, sourceRoot: sourceMapJson.sourceRoot });
// Copy sourcesContent
for (let i = 0; i < sourceMapJson.sources.length; i++) {
@@ -267,15 +291,19 @@ export function adjustSourceMap(
}
}
// Walk every mapping, adjust the generated column, and add to the new generator
// Walk every mapping, convert old generated position → byte offset → adjust → new position
consumer.eachMapping(mapping => {
const lineEdits = editsByLine.get(mapping.generatedLine - 1); // 0-based for our data
const adjustedCol = adjustColumn(mapping.generatedColumn, lineEdits);
const oldLine0 = mapping.generatedLine - 1; // 0-based
const oldOff = (oldLine0 < oldLineStarts.length
? oldLineStarts[oldLine0]
: oldLineStarts[oldLineStarts.length - 1]) + mapping.generatedColumn;
const newOff = adjustOffset(oldOff);
const newPos = offsetToLineCol(newLineStarts, newOff);
// Some mappings may be unmapped (no original position/source) - skip those.
if (mapping.source !== null && mapping.originalLine !== null && mapping.originalColumn !== null) {
const newMapping: Mapping = {
generated: { line: mapping.generatedLine, column: adjustedCol },
generated: { line: newPos.line + 1, column: newPos.col },
original: { line: mapping.originalLine, column: mapping.originalColumn },
source: mapping.source,
};
@@ -283,25 +311,82 @@ export function adjustSourceMap(
newMapping.name = mapping.name;
}
generator.addMapping(newMapping);
} else {
// Preserve unmapped segments (generated-only mappings with no original
// position). These create essential "gaps" that prevent
// originalPositionFor() from wrongly interpolating between distant
// valid mappings on the same line in minified output.
// eslint-disable-next-line local/code-no-dangerous-type-assertions
generator.addMapping({
generated: { line: newPos.line + 1, column: newPos.col },
} as Mapping);
}
});
return JSON.parse(generator.toString());
}
function adjustColumn(col: number, lineEdits: { col: number; origLen: number; newLen: number }[] | undefined): number {
if (!lineEdits) {
return col;
}
let shift = 0;
for (const edit of lineEdits) {
if (edit.col + edit.origLen <= col) {
shift += edit.newLen - edit.origLen;
} else if (edit.col < col) {
return edit.col + shift;
} else {
function buildLineStarts(text: string): number[] {
const starts: number[] = [0];
let pos = 0;
while (true) {
const nl = text.indexOf('\n', pos);
if (nl === -1) {
break;
}
starts.push(nl + 1);
pos = nl + 1;
}
return col + shift;
return starts;
}
/**
* Compute line starts for the code that results from applying `edits` to
* `originalCode`, without materialising the full new string.
*/
function buildLineStartsAfterEdits(originalCode: string, edits: readonly TextEdit[]): number[] {
const starts: number[] = [0];
let oldPos = 0;
let newPos = 0;
for (const edit of edits) {
// Scan unchanged region [oldPos, edit.start) for newlines
let from = oldPos;
while (true) {
const nl = originalCode.indexOf('\n', from);
if (nl === -1 || nl >= edit.start) {
break;
}
starts.push(newPos + (nl - oldPos) + 1);
from = nl + 1;
}
newPos += edit.start - oldPos;
// Scan replacement text for newlines
let replFrom = 0;
while (true) {
const nl = edit.newText.indexOf('\n', replFrom);
if (nl === -1) {
break;
}
starts.push(newPos + nl + 1);
replFrom = nl + 1;
}
newPos += edit.newText.length;
oldPos = edit.end;
}
// Scan remaining unchanged text after last edit
let from = oldPos;
while (true) {
const nl = originalCode.indexOf('\n', from);
if (nl === -1) {
break;
}
starts.push(newPos + (nl - oldPos) + 1);
from = nl + 1;
}
return starts;
}
+91 -2
View File
@@ -10,6 +10,7 @@ import * as fs from 'fs';
import * as os from 'os';
import { type RawSourceMap, SourceMapConsumer } from 'source-map';
import { nlsPlugin, createNLSCollector, finalizeNLS, postProcessNLS } from '../nls-plugin.ts';
import { adjustSourceMap } from '../private-to-property.ts';
// analyzeLocalizeCalls requires the import path to end with `/nls`
const NLS_STUB = [
@@ -36,7 +37,7 @@ interface BundleResult {
async function bundleWithNLS(
files: Record<string, string>,
entryPoint: string,
opts?: { postProcess?: boolean }
opts?: { postProcess?: boolean; minify?: boolean }
): Promise<BundleResult> {
const tmpDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'nls-sm-test-'));
const srcDir = path.join(tmpDir, 'src');
@@ -64,6 +65,7 @@ async function bundleWithNLS(
packages: 'external',
sourcemap: 'linked',
sourcesContent: true,
minify: opts?.minify ?? false,
write: false,
plugins: [
nlsPlugin({ baseDir: srcDir, collector }),
@@ -91,7 +93,16 @@ async function bundleWithNLS(
// Optionally apply NLS post-processing (replaces placeholders with indices)
if (opts?.postProcess) {
const nlsResult = await finalizeNLS(collector, outDir);
jsContent = postProcessNLS(jsContent, nlsResult.indexMap, false);
const preNLSCode = jsContent;
const nlsProcessed = postProcessNLS(jsContent, nlsResult.indexMap, false);
jsContent = nlsProcessed.code;
// Adjust source map for NLS edits
if (nlsProcessed.edits.length > 0) {
const mapJson = JSON.parse(mapContent);
const adjusted = adjustSourceMap(mapJson, preNLSCode, nlsProcessed.edits);
mapContent = JSON.stringify(adjusted);
}
}
assert.ok(jsContent, 'Expected JS output');
@@ -370,4 +381,82 @@ suite('NLS plugin source maps', () => {
cleanup();
}
});
test('post-processed NLS - column mappings correct after placeholder replacement', async () => {
// NLS placeholders like "%%NLS:test/drift#k%%" are much longer than their
// replacements (e.g. "0"). Without source map adjustment the columns for
// tokens AFTER the replacement drift by the cumulative length delta.
const source = [
'import { localize } from "../vs/nls";', // 1
'export const a = localize("k1", "Alpha"); export const MARKER = "FINDME";', // 2
].join('\n');
const { js, map, cleanup } = await bundleWithNLS(
{ 'test/drift.ts': source },
'test/drift.ts',
{ postProcess: true }
);
try {
assert.ok(!js.includes('%%NLS:'), 'Placeholders should be replaced');
const bundleLine = findLine(js, 'FINDME');
const bundleCol = findColumn(js, '"FINDME"');
const pos = map.originalPositionFor({ line: bundleLine, column: bundleCol });
assert.ok(pos.source, 'Should have source');
assert.strictEqual(pos.line, 2, 'Should map to line 2');
const originalCol = findColumn(source, '"FINDME"');
const columnDrift = Math.abs(pos.column! - originalCol);
assert.ok(columnDrift <= 20,
`Column drift after NLS post-processing should be small. ` +
`Expected ~${originalCol}, got ${pos.column} (drift: ${columnDrift}). ` +
`Large drift means postProcessNLS edits were not applied to the source map.`);
} finally {
cleanup();
}
});
test('minified bundle with NLS - end-to-end column mapping', async () => {
// With minification, the entire output is (roughly) on one line.
// Multiple NLS replacements compound their column shifts. A function
// defined after several localize() calls must still map correctly.
const source = [
'import { localize } from "../vs/nls";', // 1
'', // 2
'export const a = localize("k1", "Alpha message");', // 3
'export const b = localize("k2", "Bravo message that is quite long");', // 4
'export const c = localize("k3", "Charlie");', // 5
'export const d = localize("k4", "Delta is the fourth letter");', // 6
'', // 7
'export function computeResult(x: number): number {', // 8
'\treturn x * 42;', // 9
'}', // 10
].join('\n');
const { js, map, cleanup } = await bundleWithNLS(
{ 'test/minified.ts': source },
'test/minified.ts',
{ postProcess: true, minify: true }
);
try {
assert.ok(!js.includes('%%NLS:'), 'Placeholders should be replaced');
// Find the computeResult function in the minified output.
// esbuild minifies `x * 42` and may rename the parameter, so
// search for `*42` which survives both minification and renaming.
const needle = '*42';
const bundleLine = findLine(js, needle);
const bundleCol = findColumn(js, needle);
const pos = map.originalPositionFor({ line: bundleLine, column: bundleCol });
assert.ok(pos.source, 'Should have source for minified mapping');
assert.strictEqual(pos.line, 9,
`Should map "*42" back to line 9. Got line ${pos.line}.`);
} finally {
cleanup();
}
});
});
@@ -439,6 +439,41 @@ suite('adjustSourceMap', () => {
assert.strictEqual(pos.column, origGetValueCol, 'getValue column should match original');
});
test('multi-line edit: removing newlines shifts subsequent lines up', () => {
// Simulates the NLS scenario: a template literal with embedded newlines
// is replaced with `null`, collapsing 3 lines into 1.
const code = [
'var a = "hello";', // line 0 (0-based)
'var b = `line1', // line 1
'line2', // line 2
'line3`;', // line 3
'var c = "world";', // line 4
].join('\n');
const map = createIdentitySourceMap(code, 'test.js');
// Replace the template literal `line1\nline2\nline3` with `null`
// (keeps `var b = ` and `;` intact)
const tplStart = code.indexOf('`line1');
const tplEnd = code.indexOf('line3`') + 'line3`'.length;
const edits = [{ start: tplStart, end: tplEnd, newText: 'null' }];
const result = adjustSourceMap(map, code, edits);
const consumer = new SourceMapConsumer(result);
// After edit, code is:
// "var a = \"hello\";\nvar b = null;\nvar c = \"world\";"
// "var c" was on line 5 (1-based), now on line 3 (1-based) since 2 newlines removed
// 'var c' at original line 5, col 0 should now map at generated line 3
const pos = consumer.originalPositionFor({ line: 3, column: 0 });
assert.strictEqual(pos.line, 5, 'var c should map to original line 5');
assert.strictEqual(pos.column, 0, 'var c column should be 0');
// 'var a' on line 1 should be unaffected
const posA = consumer.originalPositionFor({ line: 1, column: 0 });
assert.strictEqual(posA.line, 1, 'var a should still map to original line 1');
});
test('brand check: #field in obj -> string replacement adjusts map', () => {
const code = 'class C { #x; check(o) { return #x in o; } }';
const map = createIdentitySourceMap(code, 'test.js');
+70 -5
View File
@@ -37,7 +37,7 @@ In [gulpfile.vscode.ts](../gulpfile.vscode.ts#L228-L242), the `core-ci` task use
- `runEsbuildTranspile()` → transpile command
- `runEsbuildBundle()` → bundle command
Old gulp-based bundling renamed to `core-ci-OLD`.
Old gulp-based bundling renamed to `core-ci-old`.
---
@@ -134,7 +134,7 @@ npm run gulp vscode-reh-web-darwin-arm64-min
1. **`BUILD_INSERT_PACKAGE_CONFIGURATION`** - Server bootstrap files ([bootstrap-meta.ts](../../src/bootstrap-meta.ts)) have this marker for package.json injection. Currently handled by [inlineMeta.ts](../lib/inlineMeta.ts) in the old build's packaging step.
2. **Mangling** - The new build doesn't do TypeScript-based mangling yet. Old `core-ci` with mangling is now `core-ci-OLD`.
2. **Mangling** - The new build doesn't do TypeScript-based mangling yet. Old `core-ci` with mangling is now `core-ci-old`.
3. **Entry point duplication** - Entry points are duplicated between [buildfile.ts](../buildfile.ts) and [index.ts](index.ts). Consider consolidating.
@@ -222,13 +222,13 @@ Two categories of corruption:
2. **`--source-map-base-url` option** - Rewrites `sourceMappingURL` comments to point to CDN URLs.
3. **NLS plugin inline source maps** (`nls-plugin.ts`) - The `onLoad` handler now generates an inline source map (`//# sourceMappingURL=data:...`) mapping from NLS-transformed source back to original. esbuild composes this with its own bundle source map. `SourceMapGenerator.setSourceContent` embeds the original source so `sourcesContent` in the final `.map` has the real TypeScript. Tests in `test/nls-sourcemap.test.ts`.
3. **NLS plugin inline source maps** (`nls-plugin.ts`) - The `onLoad` handler generates an inline source map (`//# sourceMappingURL=data:...`) mapping from NLS-transformed source back to original. esbuild composes this with its own bundle source map. `SourceMapGenerator.setSourceContent` embeds the original source so `sourcesContent` in the final `.map` has the real TypeScript. `generateNLSSourceMap` adds per-column identity mappings after each edit on a line so that esbuild's source-map composition preserves fine-grained column accuracy (source maps don't interpolate columns — they use binary search, so a single boundary mapping would collapse all subsequent columns to the edit-end position). Tests in `test/nls-sourcemap.test.ts`.
4. **`convertPrivateFields` source map adjustment** (`private-to-property.ts`) - `convertPrivateFields` returns its sorted edits as `TextEdit[]`. `adjustSourceMap()` uses `SourceMapConsumer` to walk every mapping, adjusts generated columns based on cumulative edit shifts per line, and rebuilds with `SourceMapGenerator`. The post-processing loop in `index.ts` saves pre-mangle content + edits per JS file, then applies `adjustSourceMap` to the corresponding `.map`. Tests in `test/private-to-property.test.ts`.
### Not Yet Fixed
5. **`postProcessNLS` source map adjustment** (`nls-plugin.ts`, `index.ts`) — `postProcessNLS` now returns `{ code, edits }` where `edits` is a `TextEdit[]` tracking each replacement's byte offset. The bundle loop in `index.ts` chains `adjustSourceMap` calls: first for mangle edits, then for NLS edits, so both transforms are accurately reflected in the final `.map` file. Tests in `test/nls-sourcemap.test.ts`.
**`postProcessNLS` column drift** - Replaces NLS placeholders with short indices in bundled output without updating `.map` files. Shifts columns but never lines, so line-level debugging and crash reporting work correctly. Fixing would require tracking replacement offsets through regex matches and adjusting the source map, similar to `adjustSourceMap`.
6. **`adjustSourceMap` unmapped segment preservation** (`private-to-property.ts`) — Previously, `adjustSourceMap()` silently dropped mappings where `source === null`. These unmapped segments create essential "gaps" that prevent `originalPositionFor()` from wrongly interpolating between distant valid mappings on the same minified line. Now emits them as generated-only mappings. Also preserves `sourceRoot` from the input map.
### Key Technical Details
@@ -241,6 +241,71 @@ Two categories of corruption:
**Plugin interaction:** Both the NLS plugin and `fileContentMapperPlugin` register `onLoad({ filter: /\.ts$/ })`. In esbuild, the first `onLoad` to return non-`undefined` wins. The NLS plugin is `unshift`ed (runs first), so files with NLS calls skip `fileContentMapperPlugin`. This is safe in practice since `product.ts` (which has `BUILD->INSERT_PRODUCT_CONFIGURATION`) has no localize calls.
### Still Broken — Full Production Build (`npm run gulp vscode-min`)
**Symptom:** Source maps are totally broken in the minified production build. E.g. a breakpoint at `src/vs/editor/browser/editorExtensions.ts` line 308 resolves to `src/vs/editor/common/cursor/cursorMoveCommands.ts` line 732 — a completely different file. This is **cross-file** mapping corruption, not just column drift.
**Status of unit tests:** The fixes above pass in isolated unit tests (small 12 file bundles via `esbuild.build` with `minify: true`). The tests verify column drift ≤ 20 and correct line mapping for single-file bundles with NLS. **183 tests pass, 0 failing.** But the full production build bundles hundreds of files into huge minified outputs (e.g. `workbench.desktop.main.js` at ~15 MB) and the source maps break at that scale.
**Suspected root causes (need investigation):**
1. **`generateNLSSourceMap` per-column identity mappings may overwhelm esbuild's source-map composition.** The fix added one mapping per column from edit-end to end-of-line (or next edit). For a long TypeScript line with a `localize()` call near the beginning, this generates hundreds of identity mappings per line. Across hundreds of files, the inline source maps embedded in `onLoad` responses may be extremely large. esbuild must compose these with its own source maps during bundling — it may hit limits, silently drop mappings, or produce incorrect composed maps at this scale. **Mitigation to try:** Instead of per-column mappings, use sparser "checkpoint" mappings (e.g., every N characters) or rely only on boundary mappings and accept some column drift within the NLS-transformed region. The old boundary-only approach was wrong (collapsed all downstream columns), but per-column may be the other extreme.
2. **`adjustSourceMap` may corrupt source indices in large minified bundles.** In a minified bundle, the entire output is on one or very few lines. `adjustSourceMap()` walks every mapping via `SourceMapConsumer.eachMapping()` and adjusts `generatedColumn` using `adjustColumn()`. But when thousands of mappings all share `generatedLine: 1` and there are hundreds of NLS edits on that same line, there may be sorting/ordering bugs: `eachMapping()` returns mappings in generated order by default, but `adjustColumn()` binary-searches through edits sorted by column. If edits cover regions that interleave with mappings from different source files, the cumulative shift calculation might produce wrong columns that then resolve to wrong source files.
3. **Chained `adjustSourceMap` calls (mangle → NLS) may compound errors.** After the first `adjustSourceMap` for mangle edits, the source map's generated columns are updated. The second call for NLS edits uses `nlsEdits` which were computed against `preNLSCode` — but `preNLSCode` is the post-mangle JS, which is what the first `adjustSourceMap` maps from. This chaining _should_ be correct, but needs verification at scale with a real minified bundle.
4. **The `source-map` v0.6.1 library may have precision issues with very large VLQ-encoded maps.** The bundled outputs have source maps with hundreds of thousands of mappings. The library is old (2017) and there may be numerical precision or sorting issues with very large maps. Consider testing with `source-map` v0.7+ or the Rust-based `@aspect-build/source-map`.
5. **Alternative approach: skip per-column NLS plugin mappings, fix only `postProcessNLS`.** The NLS plugin `onLoad` replaces `"key"` with `"%%NLS:longPlaceholder%%"` — a length change that only affects columns on affected lines. The subsequent `postProcessNLS` then replaces the long placeholder with a short index. If the `adjustSourceMap` for `postProcessNLS` is correct, it should compensate for both expansions (plugin expansion + post-process contraction). We might not need per-column mappings in `generateNLSSourceMap` at all — just the boundary mapping. The column will drift in the intermediate representation but `adjustSourceMap` for NLS should fix it. **This hypothesis needs testing.**
6. **Alternative approach: do NLS replacement purely in post-processing.** Skip the `onLoad` two-phase approach (placeholder insertion + post-processing replacement) entirely. Instead, run `postProcessNLS` as a single post-processing step that directly replaces `localize("key", "message")``localize(0, null)` in the bundled JS output, with proper source-map adjustment via `adjustSourceMap`. This avoids both the inline source map composition complexity and the two-step replacement. The downside is that post-processing must parse/regex-match real `localize()` calls (not easy placeholders), which is more fragile.
**Summary of fixes applied vs status:**
| Bug | Fix | Unit test | Production |
|-----|-----|-----------|------------|
| `generateNLSSourceMap` only had boundary mappings → columns collapsed | Added per-column identity mappings after each edit | Pass (drift: 0) | **Broken** — may overwhelm esbuild composition at scale |
| `postProcessNLS` didn't track edits for source map adjustment | Returns `{ code, edits }`, chained in `index.ts` | Pass | **Broken**`adjustSourceMap` may corrupt source indices on huge single-line minified output |
| `adjustSourceMap` dropped unmapped segments | Preserves generated-only mappings + `sourceRoot` | Pass (no regressions) | **Broken** — same cross-file mapping issue |
**Files involved:**
- `build/next/nls-plugin.ts``generateNLSSourceMap()` (per-column mappings), `postProcessNLS()` (returns edits), `replaceInOutput()` (regex replacement)
- `build/next/private-to-property.ts``adjustSourceMap()` (column adjustment)
- `build/next/index.ts` — bundle post-processing loop (lines ~899975), chains adjustSourceMap calls
- `build/next/test/nls-sourcemap.test.ts` — unit tests (pass but don't cover production-scale bundles)
**How to reproduce:**
```bash
npm run gulp vscode-min
# Open out-vscode-min/ in a debugger, set breakpoints in editor files
# Observe breakpoints resolve to wrong files
```
**How to debug further:**
```bash
# 1. Build with just --nls (no mangle) to isolate NLS from mangle issues
npx tsx build/next/index.ts bundle --nls --minify --target desktop --out out-debug
# 2. Build with just --mangle-privates (no NLS) to isolate mangle issues
npx tsx build/next/index.ts bundle --mangle-privates --minify --target desktop --out out-debug
# 3. Build with neither (baseline — does esbuild's own map work?)
npx tsx build/next/index.ts bundle --minify --target desktop --out out-debug
# 4. Compare .map files across the three builds to find where mappings diverge
# 5. Validate a specific mapping in the large bundle:
node -e "
const {SourceMapConsumer} = require('source-map');
const fs = require('fs');
const map = JSON.parse(fs.readFileSync('./out-debug/vs/workbench/workbench.desktop.main.js.map','utf8'));
const c = new SourceMapConsumer(map);
// Look up a known position and see which source file it resolves to
console.log(c.originalPositionFor({line: 1, column: XXXX}));
"
```
---
## Self-hosting Setup
+1
View File
@@ -60,6 +60,7 @@ export const dirs = [
'test/mcp',
'.vscode/extensions/vscode-selfhost-import-aid',
'.vscode/extensions/vscode-selfhost-test-provider',
'.vscode/extensions/vscode-extras',
];
if (existsSync(`${import.meta.dirname}/../../.build/distro/npm`)) {
+22
View File
@@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as child_process from 'child_process';
import { root, isUpToDate, forceInstallMessage } from './installStateHash.ts';
if (!process.argv.includes('--force') && isUpToDate()) {
console.log(`\x1b[32mAll dependencies up to date.\x1b[0m ${forceInstallMessage}`);
process.exit(0);
}
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const result = child_process.spawnSync(npm, ['install'], {
cwd: root,
stdio: 'inherit',
shell: true,
env: { ...process.env, VSCODE_FORCE_INSTALL: '1' },
});
process.exit(result.status ?? 1);
+4 -4
View File
@@ -526,13 +526,13 @@
}
},
"node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
"brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
+151
View File
@@ -0,0 +1,151 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as crypto from 'crypto';
import * as fs from 'fs';
import path from 'path';
import { dirs } from './dirs.ts';
export const root = fs.realpathSync.native(path.dirname(path.dirname(import.meta.dirname)));
export const stateFile = path.join(root, 'node_modules', '.postinstall-state');
export const stateContentsFile = path.join(root, 'node_modules', '.postinstall-state-contents');
export const forceInstallMessage = 'Run \x1b[36mnode build/npm/fast-install.ts --force\x1b[0m to force a full install.';
export function collectInputFiles(): string[] {
const files: string[] = [];
for (const dir of dirs) {
const base = dir === '' ? root : path.join(root, dir);
for (const file of ['package.json', 'package-lock.json', '.npmrc']) {
const filePath = path.join(base, file);
if (fs.existsSync(filePath)) {
files.push(filePath);
}
}
}
files.push(path.join(root, '.nvmrc'));
return files;
}
export interface PostinstallState {
readonly nodeVersion: string;
readonly fileHashes: Record<string, string>;
}
const packageJsonRelevantKeys = new Set([
'name',
'dependencies',
'devDependencies',
'optionalDependencies',
'peerDependencies',
'peerDependenciesMeta',
'overrides',
'engines',
'workspaces',
'bundledDependencies',
'bundleDependencies',
]);
const packageLockJsonIgnoredKeys = new Set(['version']);
function normalizeFileContent(filePath: string): string {
const raw = fs.readFileSync(filePath, 'utf8');
const basename = path.basename(filePath);
if (basename === 'package.json') {
const json = JSON.parse(raw);
const filtered: Record<string, unknown> = {};
for (const key of packageJsonRelevantKeys) {
// eslint-disable-next-line local/code-no-in-operator
if (key in json) {
filtered[key] = json[key];
}
}
return JSON.stringify(filtered, null, '\t') + '\n';
}
if (basename === 'package-lock.json') {
const json = JSON.parse(raw);
for (const key of packageLockJsonIgnoredKeys) {
delete json[key];
}
if (json.packages?.['']) {
for (const key of packageLockJsonIgnoredKeys) {
delete json.packages[''][key];
}
}
return JSON.stringify(json, null, '\t') + '\n';
}
return raw;
}
function hashContent(content: string): string {
const hash = crypto.createHash('sha256');
hash.update(content);
return hash.digest('hex');
}
export function computeState(): PostinstallState {
const fileHashes: Record<string, string> = {};
for (const filePath of collectInputFiles()) {
const key = path.relative(root, filePath);
try {
fileHashes[key] = hashContent(normalizeFileContent(filePath));
} catch {
// file may not be readable
}
}
return { nodeVersion: process.versions.node, fileHashes };
}
export function computeContents(): Record<string, string> {
const fileContents: Record<string, string> = {};
for (const filePath of collectInputFiles()) {
try {
fileContents[path.relative(root, filePath)] = normalizeFileContent(filePath);
} catch {
// file may not be readable
}
}
return fileContents;
}
export function readSavedState(): PostinstallState | undefined {
try {
const { nodeVersion, fileHashes } = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
return { nodeVersion, fileHashes };
} catch {
return undefined;
}
}
export function isUpToDate(): boolean {
const saved = readSavedState();
if (!saved) {
return false;
}
const current = computeState();
return saved.nodeVersion === current.nodeVersion
&& JSON.stringify(saved.fileHashes) === JSON.stringify(current.fileHashes);
}
export function readSavedContents(): Record<string, string> | undefined {
try {
return JSON.parse(fs.readFileSync(stateContentsFile, 'utf8'));
} catch {
return undefined;
}
}
// When run directly, output state as JSON for tooling (e.g. the vscode-extras extension).
if (import.meta.filename === process.argv[1]) {
console.log(JSON.stringify({
root,
stateContentsFile,
current: computeState(),
saved: readSavedState(),
files: [...collectInputFiles(), stateFile],
}));
}
+143 -67
View File
@@ -8,9 +8,9 @@ import path from 'path';
import * as os from 'os';
import * as child_process from 'child_process';
import { dirs } from './dirs.ts';
import { root, stateFile, stateContentsFile, computeState, computeContents, isUpToDate } from './installStateHash.ts';
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
const root = path.dirname(path.dirname(import.meta.dirname));
const rootNpmrcConfigKeys = getNpmrcConfigKeys(path.join(root, '.npmrc'));
function log(dir: string, message: string) {
@@ -35,24 +35,45 @@ function run(command: string, args: string[], opts: child_process.SpawnSyncOptio
}
}
function npmInstall(dir: string, opts?: child_process.SpawnSyncOptions) {
opts = {
function spawnAsync(command: string, args: string[], opts: child_process.SpawnOptions): Promise<string> {
return new Promise((resolve, reject) => {
const child = child_process.spawn(command, args, { ...opts, stdio: ['ignore', 'pipe', 'pipe'] });
let output = '';
child.stdout?.on('data', (data: Buffer) => { output += data.toString(); });
child.stderr?.on('data', (data: Buffer) => { output += data.toString(); });
child.on('error', reject);
child.on('close', (code) => {
if (code !== 0) {
reject(new Error(`Process exited with code: ${code}\n${output}`));
} else {
resolve(output);
}
});
});
}
async function npmInstallAsync(dir: string, opts?: child_process.SpawnOptions): Promise<void> {
const finalOpts: child_process.SpawnOptions = {
env: { ...process.env },
...(opts ?? {}),
cwd: dir,
stdio: 'inherit',
shell: true
cwd: path.join(root, dir),
shell: true,
};
const command = process.env['npm_command'] || 'install';
if (process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'] && /^(.build\/distro\/npm\/)?remote$/.test(dir)) {
const syncOpts: child_process.SpawnSyncOptions = {
env: finalOpts.env,
cwd: root,
stdio: 'inherit',
shell: true,
};
const userinfo = os.userInfo();
log(dir, `Installing dependencies inside container ${process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME']}...`);
opts.cwd = root;
if (process.env['npm_config_arch'] === 'arm64') {
run('sudo', ['docker', 'run', '--rm', '--privileged', 'multiarch/qemu-user-static', '--reset', '-p', 'yes'], opts);
run('sudo', ['docker', 'run', '--rm', '--privileged', 'multiarch/qemu-user-static', '--reset', '-p', 'yes'], syncOpts);
}
run('sudo', [
'docker', 'run',
@@ -63,11 +84,16 @@ function npmInstall(dir: string, opts?: child_process.SpawnSyncOptions) {
'-w', path.resolve('/root/vscode', dir),
process.env['VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME'],
'sh', '-c', `\"chown -R root:root ${path.resolve('/root/vscode', dir)} && export PATH="/root/vscode/.build/nodejs-musl/usr/local/bin:$PATH" && npm i -g node-gyp-build && npm ci\"`
], opts);
run('sudo', ['chown', '-R', `${userinfo.uid}:${userinfo.gid}`, `${path.resolve(root, dir)}`], opts);
], syncOpts);
run('sudo', ['chown', '-R', `${userinfo.uid}:${userinfo.gid}`, `${path.resolve(root, dir)}`], syncOpts);
} else {
log(dir, 'Installing dependencies...');
run(npm, command.split(' '), opts);
const output = await spawnAsync(npm, command.split(' '), finalOpts);
if (output.trim()) {
for (const line of output.trim().split('\n')) {
log(dir, line);
}
}
}
removeParcelWatcherPrebuild(dir);
}
@@ -156,65 +182,115 @@ function clearInheritedNpmrcConfig(dir: string, env: NodeJS.ProcessEnv): void {
}
}
for (const dir of dirs) {
async function runWithConcurrency(tasks: (() => Promise<void>)[], concurrency: number): Promise<void> {
const errors: Error[] = [];
let index = 0;
if (dir === '') {
removeParcelWatcherPrebuild(dir);
continue; // already executed in root
}
let opts: child_process.SpawnSyncOptions | undefined;
if (dir === 'build') {
opts = {
env: {
...process.env
},
};
if (process.env['CC']) { opts.env!['CC'] = 'gcc'; }
if (process.env['CXX']) { opts.env!['CXX'] = 'g++'; }
if (process.env['CXXFLAGS']) { opts.env!['CXXFLAGS'] = ''; }
if (process.env['LDFLAGS']) { opts.env!['LDFLAGS'] = ''; }
setNpmrcConfig('build', opts.env!);
npmInstall('build', opts);
continue;
}
if (/^(.build\/distro\/npm\/)?remote$/.test(dir)) {
// node modules used by vscode server
opts = {
env: {
...process.env
},
};
if (process.env['VSCODE_REMOTE_CC']) {
opts.env!['CC'] = process.env['VSCODE_REMOTE_CC'];
} else {
delete opts.env!['CC'];
async function worker() {
while (index < tasks.length) {
const i = index++;
try {
await tasks[i]();
} catch (err) {
errors.push(err as Error);
}
}
if (process.env['VSCODE_REMOTE_CXX']) {
opts.env!['CXX'] = process.env['VSCODE_REMOTE_CXX'];
} else {
delete opts.env!['CXX'];
}
if (process.env['CXXFLAGS']) { delete opts.env!['CXXFLAGS']; }
if (process.env['CFLAGS']) { delete opts.env!['CFLAGS']; }
if (process.env['LDFLAGS']) { delete opts.env!['LDFLAGS']; }
if (process.env['VSCODE_REMOTE_CXXFLAGS']) { opts.env!['CXXFLAGS'] = process.env['VSCODE_REMOTE_CXXFLAGS']; }
if (process.env['VSCODE_REMOTE_LDFLAGS']) { opts.env!['LDFLAGS'] = process.env['VSCODE_REMOTE_LDFLAGS']; }
if (process.env['VSCODE_REMOTE_NODE_GYP']) { opts.env!['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; }
setNpmrcConfig('remote', opts.env!);
npmInstall(dir, opts);
continue;
}
// For directories that don't define their own .npmrc, clear inherited config
const env = { ...process.env };
clearInheritedNpmrcConfig(dir, env);
npmInstall(dir, { env });
await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker()));
if (errors.length > 0) {
for (const err of errors) {
console.error(err.message);
}
process.exit(1);
}
}
child_process.execSync('git config pull.rebase merges');
child_process.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs');
async function main() {
if (!process.env['VSCODE_FORCE_INSTALL'] && isUpToDate()) {
log('.', 'All dependencies up to date, skipping postinstall.');
child_process.execSync('git config pull.rebase merges');
child_process.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs');
return;
}
const _state = computeState();
const nativeTasks: (() => Promise<void>)[] = [];
const parallelTasks: (() => Promise<void>)[] = [];
for (const dir of dirs) {
if (dir === '') {
removeParcelWatcherPrebuild(dir);
continue; // already executed in root
}
if (dir === 'build') {
nativeTasks.push(() => {
const env: NodeJS.ProcessEnv = { ...process.env };
if (process.env['CC']) { env['CC'] = 'gcc'; }
if (process.env['CXX']) { env['CXX'] = 'g++'; }
if (process.env['CXXFLAGS']) { env['CXXFLAGS'] = ''; }
if (process.env['LDFLAGS']) { env['LDFLAGS'] = ''; }
setNpmrcConfig('build', env);
return npmInstallAsync('build', { env });
});
continue;
}
if (/^(.build\/distro\/npm\/)?remote$/.test(dir)) {
const remoteDir = dir;
nativeTasks.push(() => {
const env: NodeJS.ProcessEnv = { ...process.env };
if (process.env['VSCODE_REMOTE_CC']) {
env['CC'] = process.env['VSCODE_REMOTE_CC'];
} else {
delete env['CC'];
}
if (process.env['VSCODE_REMOTE_CXX']) {
env['CXX'] = process.env['VSCODE_REMOTE_CXX'];
} else {
delete env['CXX'];
}
if (process.env['CXXFLAGS']) { delete env['CXXFLAGS']; }
if (process.env['CFLAGS']) { delete env['CFLAGS']; }
if (process.env['LDFLAGS']) { delete env['LDFLAGS']; }
if (process.env['VSCODE_REMOTE_CXXFLAGS']) { env['CXXFLAGS'] = process.env['VSCODE_REMOTE_CXXFLAGS']; }
if (process.env['VSCODE_REMOTE_LDFLAGS']) { env['LDFLAGS'] = process.env['VSCODE_REMOTE_LDFLAGS']; }
if (process.env['VSCODE_REMOTE_NODE_GYP']) { env['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; }
setNpmrcConfig('remote', env);
return npmInstallAsync(remoteDir, { env });
});
continue;
}
const taskDir = dir;
parallelTasks.push(() => {
const env = { ...process.env };
clearInheritedNpmrcConfig(taskDir, env);
return npmInstallAsync(taskDir, { env });
});
}
// Native dirs (build, remote) run sequentially to avoid node-gyp conflicts
for (const task of nativeTasks) {
await task();
}
// JS-only dirs run in parallel
const concurrency = Math.min(os.cpus().length, 8);
log('.', `Running ${parallelTasks.length} npm installs with concurrency ${concurrency}...`);
await runWithConcurrency(parallelTasks, concurrency);
child_process.execSync('git config pull.rebase merges');
child_process.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs');
fs.writeFileSync(stateFile, JSON.stringify(_state));
fs.writeFileSync(stateContentsFile, JSON.stringify(computeContents()));
}
main().catch(err => {
console.error(err);
process.exit(1);
});
+8
View File
@@ -6,6 +6,7 @@ import path from 'path';
import * as fs from 'fs';
import * as child_process from 'child_process';
import * as os from 'os';
import { isUpToDate, forceInstallMessage } from './installStateHash.ts';
if (!process.env['VSCODE_SKIP_NODE_VERSION_CHECK']) {
// Get the running Node.js version
@@ -41,6 +42,13 @@ if (process.env.npm_execpath?.includes('yarn')) {
throw new Error();
}
// Fast path: if nothing changed since last successful install, skip everything.
// This makes `npm i` near-instant when dependencies haven't changed.
if (!process.env['VSCODE_FORCE_INSTALL'] && isUpToDate()) {
console.log(`\x1b[32mAll dependencies up to date.\x1b[0m ${forceInstallMessage}`);
process.exit(0);
}
if (process.platform === 'win32') {
if (!hasSupportedVisualStudioVersion()) {
console.error('\x1b[1;31m*** Invalid C/C++ Compiler Toolchain. Please check https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites.\x1b[0;0m');
+60 -44
View File
@@ -1027,29 +1027,6 @@
"node": ">=18"
}
},
"node_modules/@isaacs/balanced-match": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@isaacs/brace-expansion": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@isaacs/balanced-match": "^4.0.1"
},
"engines": {
"node": "20 || >=22"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -2170,6 +2147,29 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/@vscode/vsce/node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@vscode/vsce/node_modules/brace-expansion": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/@vscode/vsce/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2232,16 +2232,16 @@
}
},
"node_modules/@vscode/vsce/node_modules/glob/node_modules/minimatch": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz",
"integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/brace-expansion": "^5.0.0"
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "20 || >=22"
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -2318,9 +2318,9 @@
}
},
"node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3493,10 +3493,23 @@
],
"license": "BSD-3-Clause"
},
"node_modules/fast-xml-builder": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.0.0.tgz",
"integrity": "sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT"
},
"node_modules/fast-xml-parser": {
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.6.tgz",
"integrity": "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==",
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz",
"integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==",
"dev": true,
"funding": [
{
@@ -3506,6 +3519,7 @@
],
"license": "MIT",
"dependencies": {
"fast-xml-builder": "^1.0.0",
"strnum": "^2.1.2"
},
"bin": {
@@ -4512,9 +4526,9 @@
}
},
"node_modules/markdown-it": {
"version": "14.1.0",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"version": "14.1.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
"integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4654,10 +4668,11 @@
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -6743,12 +6758,13 @@
}
},
"node_modules/vscode-universal-bundler/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
"brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
+113 -111
View File
@@ -8,8 +8,8 @@
"name": "@vscode/sample-source",
"version": "0.0.0",
"devDependencies": {
"@vscode/component-explorer": "^0.1.1-12",
"@vscode/component-explorer-vite-plugin": "^0.1.1-12",
"@vscode/component-explorer": "^0.1.1-19",
"@vscode/component-explorer-vite-plugin": "^0.1.1-19",
"@vscode/rollup-plugin-esm-url": "^1.0.1-1",
"rollup": "*",
"vite": "npm:rolldown-vite@latest"
@@ -315,9 +315,9 @@
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz",
"integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
"integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
"cpu": [
"arm"
],
@@ -329,9 +329,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz",
"integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
"integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
"cpu": [
"arm64"
],
@@ -343,9 +343,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz",
"integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
"integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
"cpu": [
"arm64"
],
@@ -357,9 +357,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz",
"integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
"integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
"cpu": [
"x64"
],
@@ -371,9 +371,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz",
"integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
"integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
"cpu": [
"arm64"
],
@@ -385,9 +385,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz",
"integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
"integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
"cpu": [
"x64"
],
@@ -399,9 +399,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz",
"integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
"integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
"cpu": [
"arm"
],
@@ -413,9 +413,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz",
"integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
"integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
"cpu": [
"arm"
],
@@ -427,9 +427,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz",
"integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
"integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
"cpu": [
"arm64"
],
@@ -441,9 +441,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz",
"integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
"integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
"cpu": [
"arm64"
],
@@ -455,9 +455,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz",
"integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
"integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
"cpu": [
"loong64"
],
@@ -469,9 +469,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz",
"integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
"integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
"cpu": [
"loong64"
],
@@ -483,9 +483,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz",
"integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
"integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
"cpu": [
"ppc64"
],
@@ -497,9 +497,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz",
"integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
"integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
"cpu": [
"ppc64"
],
@@ -511,9 +511,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz",
"integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
"integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
"cpu": [
"riscv64"
],
@@ -525,9 +525,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz",
"integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
"integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
"cpu": [
"riscv64"
],
@@ -539,9 +539,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz",
"integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
"integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
"cpu": [
"s390x"
],
@@ -553,9 +553,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
"integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
"integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
"cpu": [
"x64"
],
@@ -567,9 +567,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz",
"integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
"integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
"cpu": [
"x64"
],
@@ -581,9 +581,9 @@
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz",
"integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
"integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
"cpu": [
"x64"
],
@@ -595,9 +595,9 @@
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz",
"integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
"integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
"cpu": [
"arm64"
],
@@ -609,9 +609,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz",
"integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
"integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
"cpu": [
"arm64"
],
@@ -623,9 +623,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz",
"integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
"integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
"cpu": [
"ia32"
],
@@ -637,9 +637,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz",
"integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
"integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
"cpu": [
"x64"
],
@@ -651,9 +651,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz",
"integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
"integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
"cpu": [
"x64"
],
@@ -683,20 +683,22 @@
"license": "MIT"
},
"node_modules/@vscode/component-explorer": {
"version": "0.1.1-12",
"resolved": "https://registry.npmjs.org/@vscode/component-explorer/-/component-explorer-0.1.1-12.tgz",
"integrity": "sha512-qqbxbu3BvqWtwFdVsROLUSd1BiScCiUPP5n0sk0yV1WDATlAl6wQMX1QlmsZy3hag8iP/MXUEj5tSBjA1T7tFw==",
"version": "0.1.1-19",
"resolved": "https://registry.npmjs.org/@vscode/component-explorer/-/component-explorer-0.1.1-19.tgz",
"integrity": "sha512-wvcjw1A8wSH/oR5q+lZrBSyOQZfvXtLPYkQJBj11FBKu35iHko0FTIPMG25Ee+TpT2/BWLd29dWwiJODDQbC8w==",
"dev": true,
"license": "MIT",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
},
"node_modules/@vscode/component-explorer-vite-plugin": {
"version": "0.1.1-12",
"resolved": "https://registry.npmjs.org/@vscode/component-explorer-vite-plugin/-/component-explorer-vite-plugin-0.1.1-12.tgz",
"integrity": "sha512-MG5ndoooX2X9PYto1WkNSwWKKmR5OJx3cBnUf7JHm8ERw+8RsZbLe+WS+hVOqnCVPxHy7t+0IYRFl7IC5cuwOQ==",
"version": "0.1.1-19",
"resolved": "https://registry.npmjs.org/@vscode/component-explorer-vite-plugin/-/component-explorer-vite-plugin-0.1.1-19.tgz",
"integrity": "sha512-V0wMhLvHMbeUHOzwGrBPMwwvcbGhXXaQTCGc9hNfF4fjUutOtQFu5o+9XKDG1hIcKgk5qyvcRoXjVazBcg19lA==",
"dev": true,
"license": "MIT",
"dependencies": {
"tinyglobby": "^0.2.0"
},
@@ -1167,9 +1169,9 @@
}
},
"node_modules/rollup": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
"integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
"version": "4.59.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1183,31 +1185,31 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.57.1",
"@rollup/rollup-android-arm64": "4.57.1",
"@rollup/rollup-darwin-arm64": "4.57.1",
"@rollup/rollup-darwin-x64": "4.57.1",
"@rollup/rollup-freebsd-arm64": "4.57.1",
"@rollup/rollup-freebsd-x64": "4.57.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
"@rollup/rollup-linux-arm-musleabihf": "4.57.1",
"@rollup/rollup-linux-arm64-gnu": "4.57.1",
"@rollup/rollup-linux-arm64-musl": "4.57.1",
"@rollup/rollup-linux-loong64-gnu": "4.57.1",
"@rollup/rollup-linux-loong64-musl": "4.57.1",
"@rollup/rollup-linux-ppc64-gnu": "4.57.1",
"@rollup/rollup-linux-ppc64-musl": "4.57.1",
"@rollup/rollup-linux-riscv64-gnu": "4.57.1",
"@rollup/rollup-linux-riscv64-musl": "4.57.1",
"@rollup/rollup-linux-s390x-gnu": "4.57.1",
"@rollup/rollup-linux-x64-gnu": "4.57.1",
"@rollup/rollup-linux-x64-musl": "4.57.1",
"@rollup/rollup-openbsd-x64": "4.57.1",
"@rollup/rollup-openharmony-arm64": "4.57.1",
"@rollup/rollup-win32-arm64-msvc": "4.57.1",
"@rollup/rollup-win32-ia32-msvc": "4.57.1",
"@rollup/rollup-win32-x64-gnu": "4.57.1",
"@rollup/rollup-win32-x64-msvc": "4.57.1",
"@rollup/rollup-android-arm-eabi": "4.59.0",
"@rollup/rollup-android-arm64": "4.59.0",
"@rollup/rollup-darwin-arm64": "4.59.0",
"@rollup/rollup-darwin-x64": "4.59.0",
"@rollup/rollup-freebsd-arm64": "4.59.0",
"@rollup/rollup-freebsd-x64": "4.59.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
"@rollup/rollup-linux-arm-musleabihf": "4.59.0",
"@rollup/rollup-linux-arm64-gnu": "4.59.0",
"@rollup/rollup-linux-arm64-musl": "4.59.0",
"@rollup/rollup-linux-loong64-gnu": "4.59.0",
"@rollup/rollup-linux-loong64-musl": "4.59.0",
"@rollup/rollup-linux-ppc64-gnu": "4.59.0",
"@rollup/rollup-linux-ppc64-musl": "4.59.0",
"@rollup/rollup-linux-riscv64-gnu": "4.59.0",
"@rollup/rollup-linux-riscv64-musl": "4.59.0",
"@rollup/rollup-linux-s390x-gnu": "4.59.0",
"@rollup/rollup-linux-x64-gnu": "4.59.0",
"@rollup/rollup-linux-x64-musl": "4.59.0",
"@rollup/rollup-openbsd-x64": "4.59.0",
"@rollup/rollup-openharmony-arm64": "4.59.0",
"@rollup/rollup-win32-arm64-msvc": "4.59.0",
"@rollup/rollup-win32-ia32-msvc": "4.59.0",
"@rollup/rollup-win32-x64-gnu": "4.59.0",
"@rollup/rollup-win32-x64-msvc": "4.59.0",
"fsevents": "~2.3.2"
}
},
+2 -2
View File
@@ -9,8 +9,8 @@
"preview": "vite preview"
},
"devDependencies": {
"@vscode/component-explorer": "^0.1.1-12",
"@vscode/component-explorer-vite-plugin": "^0.1.1-12",
"@vscode/component-explorer": "^0.1.1-19",
"@vscode/component-explorer-vite-plugin": "^0.1.1-19",
"@vscode/rollup-plugin-esm-url": "^1.0.1-1",
"rollup": "*",
"vite": "npm:rolldown-vite@latest"
+2 -5
View File
@@ -143,11 +143,8 @@ const logger = createLogger();
const loggerWarn = logger.warn;
logger.warn = (msg, options) => {
// amdX and the baseUrl code cannot be analyzed by vite.
// However, they are not needed, so it is okay to silence the warning.
if (msg.indexOf('vs/amdX.ts') !== -1) {
return;
}
// the baseUrl code cannot be analyzed by vite.
// However, it is not needed, so it is okay to silence the warning.
if (msg.indexOf('await import(new URL(`vs/workbench/workbench.desktop.main.js`, baseUrl).href)') !== -1) {
return;
}
+19 -1
View File
@@ -95,9 +95,12 @@ Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{
Name: "{app}"; AfterInstall: DisableAppDirInheritance
[Files]
Source: "*"; Excludes: "\CodeSignSummary*.md,\tools,\tools\*,\policies,\policies\*,\appx,\appx\*,\resources\app\product.json,\{#ExeBasename}.exe,\{#ExeBasename}.VisualElementsManifest.xml,\bin,\bin\*"; DestDir: "{code:GetDestDir}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "*"; Excludes: "\CodeSignSummary*.md,\tools,\tools\*,\policies,\policies\*,\appx,\appx\*,\resources\app\product.json,\{#ExeBasename}.exe,{#ifdef ProxyExeBasename}\{#ProxyExeBasename}.exe,{#endif}\{#ExeBasename}.VisualElementsManifest.xml,\bin,\bin\*"; DestDir: "{code:GetDestDir}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#ExeBasename}.exe"; DestDir: "{code:GetDestDir}"; DestName: "{code:GetExeBasename}"; Flags: ignoreversion
Source: "{#ExeBasename}.VisualElementsManifest.xml"; DestDir: "{code:GetDestDir}"; DestName: "{code:GetVisualElementsManifest}"; Flags: ignoreversion
#ifdef ProxyExeBasename
Source: "{#ProxyExeBasename}.exe"; DestDir: "{code:GetDestDir}"; DestName: "{code:GetProxyExeBasename}"; Flags: ignoreversion
#endif
Source: "tools\*"; DestDir: "{app}\{#VersionedResourcesFolder}\tools"; Flags: ignoreversion
Source: "policies\*"; DestDir: "{code:GetDestDir}\{#VersionedResourcesFolder}\policies"; Flags: ignoreversion skipifsourcedoesntexist
Source: "bin\{#TunnelApplicationName}.exe"; DestDir: "{code:GetDestDir}\bin"; DestName: "{code:GetBinDirTunnelApplicationFilename}"; Flags: ignoreversion skipifsourcedoesntexist
@@ -113,6 +116,11 @@ Source: "appx\{#AppxPackageDll}"; DestDir: "{code:GetDestDir}\{#VersionedResourc
Name: "{group}\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; AppUserModelID: "{#AppUserId}"; Check: ShouldUpdateShortcut(ExpandConstant('{group}\{#NameLong}.lnk'))
Name: "{autodesktop}\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; Tasks: desktopicon; AppUserModelID: "{#AppUserId}"; Check: ShouldUpdateShortcut(ExpandConstant('{autodesktop}\{#NameLong}.lnk'))
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#NameLong}"; Filename: "{app}\{#ExeBasename}.exe"; Tasks: quicklaunchicon; AppUserModelID: "{#AppUserId}"; Check: ShouldUpdateShortcut(ExpandConstant('{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#NameLong}.lnk'))
#ifdef ProxyExeBasename
Name: "{group}\{#ProxyExeBasename}"; Filename: "{app}\{#ProxyExeBasename}.exe"; AppUserModelID: "{#ProxyAppUserId}"; Check: ShouldUpdateShortcut(ExpandConstant('{group}\{#ProxyExeBasename}.lnk'))
Name: "{autodesktop}\{#ProxyNameLong}"; Filename: "{app}\{#ProxyExeBasename}.exe"; Tasks: desktopicon; AppUserModelID: "{#ProxyAppUserId}"; Check: ShouldUpdateShortcut(ExpandConstant('{autodesktop}\{#ProxyNameLong}.lnk'))
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#ProxyNameLong}"; Filename: "{app}\{#ProxyExeBasename}.exe"; Tasks: quicklaunchicon; AppUserModelID: "{#ProxyAppUserId}"; Check: ShouldUpdateShortcut(ExpandConstant('{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#ProxyNameLong}.lnk'))
#endif
[Run]
Filename: "{app}\{#ExeBasename}.exe"; Description: "{cm:LaunchProgram,{#NameLong}}"; Tasks: runcode; Flags: nowait postinstall; Check: ShouldRunAfterUpdate
@@ -1562,6 +1570,16 @@ begin
Result := ExpandConstant('{#ExeBasename}.exe');
end;
#ifdef ProxyExeBasename
function GetProxyExeBasename(Value: string): string;
begin
if IsBackgroundUpdate() and IsVersionedUpdate() then
Result := ExpandConstant('new_{#ProxyExeBasename}.exe')
else
Result := ExpandConstant('{#ProxyExeBasename}.exe');
end;
#endif
function GetBinDirTunnelApplicationFilename(Value: string): string;
begin
if IsBackgroundUpdate() and IsVersionedUpdate() then
Binary file not shown.
-58
View File
@@ -707,64 +707,6 @@
"For more information, please refer to <http://unlicense.org>"
]
},
{
"name": "@isaacs/balanced-match",
"fullLicenseText": [
"MIT License",
"",
"Copyright Isaac Z. Schlueter <i@izs.me>",
"",
"Original code Copyright Julian Gruber <julian@juliangruber.com>",
"",
"Port to TypeScript Copyright Isaac Z. Schlueter <i@izs.me>",
"",
"Permission is hereby granted, free of charge, to any person obtaining a copy of",
"this software and associated documentation files (the \"Software\"), to deal in",
"the Software without restriction, including without limitation the rights to",
"use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies",
"of the Software, and to permit persons to whom the Software is furnished to do",
"so, subject to the following conditions:",
"",
"The above copyright notice and this permission notice shall be included in all",
"copies or substantial portions of the Software.",
"",
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
"SOFTWARE.",
""
]
},
{
"name": "@isaacs/brace-expansion",
"fullLicenseText": [
"MIT License",
"",
"Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>",
"",
"Permission is hereby granted, free of charge, to any person obtaining a copy",
"of this software and associated documentation files (the \"Software\"), to deal",
"in the Software without restriction, including without limitation the rights",
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
"copies of the Software, and to permit persons to whom the Software is",
"furnished to do so, subject to the following conditions:",
"",
"The above copyright notice and this permission notice shall be included in all",
"copies or substantial portions of the Software.",
"",
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
"SOFTWARE.",
""
]
},
{
// Reason: License file starts with (MIT) before the copyright, tool can't parse it
"name": "balanced-match",
+67 -2
View File
@@ -447,6 +447,7 @@ dependencies = [
"uuid",
"winapi",
"winreg 0.50.0",
"winresource",
"zbus",
"zip",
]
@@ -2645,6 +2646,15 @@ dependencies = [
"syn 2.0.115",
]
[[package]]
name = "serde_spanned"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@@ -3004,12 +3014,36 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml"
version = "0.9.12+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
dependencies = [
"indexmap 2.13.0",
"serde_core",
"serde_spanned",
"toml_datetime 0.7.5+spec-1.1.0",
"toml_parser",
"toml_writer",
"winnow 0.7.14",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.19.15"
@@ -3017,10 +3051,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap 2.13.0",
"toml_datetime",
"winnow",
"toml_datetime 0.6.11",
"winnow 0.5.40",
]
[[package]]
name = "toml_parser"
version = "1.0.9+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
dependencies = [
"winnow 0.7.14",
]
[[package]]
name = "toml_writer"
version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
[[package]]
name = "tower-service"
version = "0.3.3"
@@ -3699,6 +3748,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
[[package]]
name = "winreg"
version = "0.8.0"
@@ -3718,6 +3773,16 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "winresource"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e287ced0f21cd11f4035fe946fd3af145f068d1acb708afd248100f89ec7432d"
dependencies = [
"toml",
"version_check",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
+140
View File
@@ -9189,6 +9189,32 @@ DEALINGS IN THE SOFTWARE.
---------------------------------------------------------
serde_spanned 1.0.4 - MIT OR Apache-2.0
https://github.com/toml-rs/toml
Copyright (c) Individual contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---------------------------------------------------------
---------------------------------------------------------
serde_urlencoded 0.7.1 - MIT/Apache-2.0
https://github.com/nox/serde_urlencoded
@@ -10517,7 +10543,34 @@ SOFTWARE.
---------------------------------------------------------
toml 0.9.12+spec-1.1.0 - MIT OR Apache-2.0
https://github.com/toml-rs/toml
Copyright (c) Individual contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---------------------------------------------------------
---------------------------------------------------------
toml_datetime 0.6.11 - MIT OR Apache-2.0
toml_datetime 0.7.5+spec-1.1.0 - MIT OR Apache-2.0
https://github.com/toml-rs/toml
../../LICENSE-MIT
@@ -10533,6 +10586,58 @@ https://github.com/toml-rs/toml
---------------------------------------------------------
toml_parser 1.0.9+spec-1.1.0 - MIT OR Apache-2.0
https://github.com/toml-rs/toml
Copyright (c) Individual contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---------------------------------------------------------
---------------------------------------------------------
toml_writer 1.0.6+spec-1.1.0 - MIT OR Apache-2.0
https://github.com/toml-rs/toml
Copyright (c) Individual contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---------------------------------------------------------
---------------------------------------------------------
tower-service 0.3.3 - MIT
https://github.com/tower-rs/tower
@@ -12700,6 +12805,7 @@ MIT License
---------------------------------------------------------
winnow 0.5.40 - MIT
winnow 0.7.14 - MIT
https://github.com/winnow-rs/winnow
The MIT License (MIT)
@@ -12755,6 +12861,40 @@ THE SOFTWARE.
---------------------------------------------------------
winresource 0.1.30 - MIT
https://github.com/BenjaminRi/winresource
The MIT License (MIT)
Copyright 2016 Max Resch
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
---------------------------------------------------------
---------------------------------------------------------
wit-bindgen 0.51.0 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT
https://github.com/bytecodealliance/wit-bindgen
+56 -1
View File
@@ -828,6 +828,36 @@ export default tseslint.config(
]
}
},
// git extension - ban non-type imports from git.d.ts (use git.constants for runtime values)
{
files: [
'extensions/git/src/**/*.ts',
],
ignores: [
'extensions/git/src/api/git.constants.ts',
],
languageOptions: {
parser: tseslint.parser,
},
plugins: {
'@typescript-eslint': tseslint.plugin,
},
rules: {
'no-restricted-imports': 'off',
'@typescript-eslint/no-restricted-imports': [
'warn',
{
'patterns': [
{
'group': ['*/api/git'],
'allowTypeImports': true,
'message': 'Use \'import type\' for types from git.d.ts and import runtime const enum values from git.constants instead'
},
]
}
]
}
},
// vscode API
{
files: [
@@ -1955,7 +1985,8 @@ export default tseslint.config(
'vs/workbench/browser/**',
'vs/workbench/contrib/**',
'vs/workbench/services/*/~',
'vs/sessions/~'
'vs/sessions/~',
'vs/sessions/services/*/~'
]
},
{
@@ -1974,6 +2005,30 @@ export default tseslint.config(
'vs/sessions/contrib/*/~'
]
},
{
'target': 'src/vs/sessions/services/*/~',
'restrictions': [
'vs/base/~',
'vs/base/parts/*/~',
'vs/platform/*/~',
'vs/editor/~',
'vs/editor/contrib/*/~',
'vs/workbench/~',
'vs/workbench/services/*/~',
{
'when': 'test',
'pattern': 'vs/workbench/contrib/*/~'
}, // TODO@layers
'tas-client', // node module allowed even in /common/
'vscode-textmate', // node module allowed even in /common/
'@vscode/vscode-languagedetection', // node module allowed even in /common/
'@vscode/tree-sitter-wasm', // type import
{
'when': 'hasBrowser',
'pattern': '@xterm/xterm'
} // node module allowed even in /browser/
]
},
]
}
},
+32
View File
@@ -0,0 +1,32 @@
# Contributing to Built-In Extensions
This directory contains built-in extensions that ship with VS Code.
## Basic Structure
A typical TypeScript-based built-in extension has the following structure:
- `package.json`: extension manifest.
- `src/`: Main directory for TypeScript source code.
- `tsconfig.json`: primary TypeScript config. This should inherit from `tsconfig.base.json`.
- `esbuild.mts`: esbuild build script used for production builds.
- `.vscodeignore`: Ignore file list. You can copy this from an existing extension.
TypeScript-based extensions have the following output structure:
- `out`: Output directory for development builds
- `dist`: Output directory for production builds.
## Enabling an Extension in the Browser
By default extensions will only target desktop. To enable an extension in browsers as well:
- Add a `"browser"` entry in `package.json` pointing to the browser bundle (for example `"./dist/browser/extension"`).
- Add `tsconfig.browser.json` that typechecks only browser-safe sources.
- Add an `esbuild.browser.mts` file. This should set `platform: 'browser'`.
Make sure the browser build of the extension only uses browser-safe APIs. If an extension needs different behavior between desktop and web, you can create distinct entrypoints for each target:
- `src/extension.ts`: Desktop entrypoint.
- `src/extension.browser.ts`: Browser entrypoint. Make sure `esbuild.browser.mts` builds this and that `tsconfig.browser.json` targets it.
+3 -3
View File
@@ -51,9 +51,9 @@
}
},
"node_modules/minimatch": {
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz",
"integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==",
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.2"
+1 -1
View File
@@ -6,7 +6,7 @@
"git": {
"name": "microsoft/vscode-css",
"repositoryUrl": "https://github.com/microsoft/vscode-css",
"commitHash": "a927fe2f73927bf5c25d0b0c4dd0e63d69fd8887"
"commitHash": "9a07d76cb0e7a56f9bfc76328a57227751e4adb4"
}
},
"licenseDetail": [
+2 -2
View File
@@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/microsoft/vscode-css/commit/a927fe2f73927bf5c25d0b0c4dd0e63d69fd8887",
"version": "https://github.com/microsoft/vscode-css/commit/9a07d76cb0e7a56f9bfc76328a57227751e4adb4",
"name": "CSS",
"scopeName": "source.css",
"patterns": [
@@ -1401,7 +1401,7 @@
"property-keywords": {
"patterns": [
{
"match": "(?xi) (?<![\\w-])\n(above|absolute|active|add|additive|after-edge|alias|all|all-petite-caps|all-scroll|all-small-caps|alpha|alphabetic|alternate|alternate-reverse\n|always|antialiased|auto|auto-fill|auto-fit|auto-pos|available|avoid|avoid-column|avoid-page|avoid-region|backwards|balance|baseline|before-edge|below|bevel\n|bidi-override|blink|block|block-axis|block-start|block-end|bold|bolder|border|border-box|both|bottom|bottom-outside|break-all|break-word|bullets\n|butt|capitalize|caption|cell|center|central|char|circle|clip|clone|close-quote|closest-corner|closest-side|col-resize|collapse|color|color-burn\n|color-dodge|column|column-reverse|common-ligatures|compact|condensed|contain|content|content-box|contents|context-menu|contextual|copy|cover\n|crisp-edges|crispEdges|crosshair|cyclic|dark|darken|dashed|decimal|default|dense|diagonal-fractions|difference|digits|disabled|disc|discretionary-ligatures\n|distribute|distribute-all-lines|distribute-letter|distribute-space|dot|dotted|double|double-circle|downleft|downright|e-resize|each-line|ease|ease-in\n|ease-in-out|ease-out|economy|ellipse|ellipsis|embed|end|evenodd|ew-resize|exact|exclude|exclusion|expanded|extends|extra-condensed|extra-expanded\n|fallback|farthest-corner|farthest-side|fill|fill-available|fill-box|filled|fit-content|fixed|flat|flex|flex-end|flex-start|flip|flow-root|forwards|freeze\n|from-image|full-width|geometricPrecision|georgian|grab|grabbing|grayscale|grid|groove|hand|hanging|hard-light|help|hidden|hide\n|historical-forms|historical-ligatures|horizontal|horizontal-tb|hue|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space\n|ideographic|inactive|infinite|inherit|initial|inline|inline-axis|inline-block|inline-end|inline-flex|inline-grid|inline-list-item|inline-start\n|inline-table|inset|inside|inter-character|inter-ideograph|inter-word|intersect|invert|isolate|isolate-override|italic|jis04|jis78|jis83\n|jis90|justify|justify-all|kannada|keep-all|landscape|large|larger|left|light|lighten|lighter|line|line-edge|line-through|linear|linearRGB\n|lining-nums|list-item|local|loose|lowercase|lr|lr-tb|ltr|luminance|luminosity|main-size|mandatory|manipulation|manual|margin-box|match-parent\n|match-source|mathematical|max-content|medium|menu|message-box|middle|min-content|miter|mixed|move|multiply|n-resize|narrower|ne-resize\n|nearest-neighbor|nesw-resize|newspaper|no-change|no-clip|no-close-quote|no-common-ligatures|no-contextual|no-discretionary-ligatures\n|no-drop|no-historical-ligatures|no-open-quote|no-repeat|none|nonzero|normal|not-allowed|nowrap|ns-resize|numbers|numeric|nw-resize|nwse-resize\n|oblique|oldstyle-nums|open|open-quote|optimizeLegibility|optimizeQuality|optimizeSpeed|optional|ordinal|outset|outside|over|overlay|overline|padding\n|padding-box|page|painted|pan-down|pan-left|pan-right|pan-up|pan-x|pan-y|paused|petite-caps|pixelated|plaintext|pointer|portrait|pre|pre-line\n|pre-wrap|preserve-3d|progress|progressive|proportional-nums|proportional-width|proximity|radial|recto|region|relative|remove|repeat|repeat-[xy]\n|reset-size|reverse|revert|ridge|right|rl|rl-tb|round|row|row-resize|row-reverse|row-severse|rtl|ruby|ruby-base|ruby-base-container|ruby-text\n|ruby-text-container|run-in|running|s-resize|saturation|scale-down|screen|scroll|scroll-position|se-resize|semi-condensed|semi-expanded|separate\n|sesame|show|sideways|sideways-left|sideways-lr|sideways-right|sideways-rl|simplified|slashed-zero|slice|small|small-caps|small-caption|smaller\n|smooth|soft-light|solid|space|space-around|space-between|space-evenly|spell-out|square|sRGB|stacked-fractions|start|static|status-bar|swap\n|step-end|step-start|sticky|stretch|strict|stroke|stroke-box|style|sub|subgrid|subpixel-antialiased|subtract|super|sw-resize|symbolic|table\n|table-caption|table-cell|table-column|table-column-group|table-footer-group|table-header-group|table-row|table-row-group|tabular-nums|tb|tb-rl\n|text|text-after-edge|text-before-edge|text-bottom|text-top|thick|thin|titling-caps|top|top-outside|touch|traditional|transparent|triangle\n|ultra-condensed|ultra-expanded|under|underline|unicase|unset|upleft|uppercase|upright|use-glyph-orientation|use-script|verso|vertical\n|vertical-ideographic|vertical-lr|vertical-rl|vertical-text|view-box|visible|visibleFill|visiblePainted|visibleStroke|w-resize|wait|wavy\n|weight|whitespace|wider|words|wrap|wrap-reverse|x|x-large|x-small|xx-large|xx-small|y|zero|zoom-in|zoom-out)\n(?![\\w-])",
"match": "(?xi) (?<![\\w-])\n(above|absolute|active|add|additive|after-edge|alias|all|all-petite-caps|all-scroll|all-small-caps|alpha|alphabetic|alternate|alternate-reverse\n|always|antialiased|auto|auto-fill|auto-fit|auto-pos|available|avoid|avoid-column|avoid-page|avoid-region|backwards|balance|baseline|before-edge|below|bevel\n|bidi-override|blink|block|block-axis|block-start|block-end|bold|bolder|border|border-box|both|bottom|bottom-outside|break-all|break-word|bullets\n|butt|capitalize|caption|cell|center|central|char|circle|clip|clone|close-quote|closest-corner|closest-side|col-resize|collapse|color|color-burn\n|color-dodge|column|column-reverse|common-ligatures|compact|condensed|contain|content|content-box|contents|context-menu|contextual|copy|cover\n|crisp-edges|crispEdges|crosshair|cyclic|dark|darken|dashed|decimal|default|dense|diagonal-fractions|difference|digits|disabled|disc|discretionary-ligatures\n|distribute|distribute-all-lines|distribute-letter|distribute-space|dot|dotted|double|double-circle|downleft|downright|e-resize|each-line|ease|ease-in\n|ease-in-out|ease-out|economy|ellipse|ellipsis|embed|end|evenodd|ew-resize|exact|exclude|exclusion|expanded|extends|extra-condensed|extra-expanded\n|fallback|farthest-corner|farthest-side|fill|fill-available|fill-box|filled|fit-content|fixed|flat|flex|flex-end|flex-start|flip|flow|flow-root|forwards|freeze\n|from-image|full-width|geometricPrecision|georgian|grab|grabbing|grayscale|grid|groove|hand|hanging|hard-light|help|hidden|hide\n|historical-forms|historical-ligatures|horizontal|horizontal-tb|hue|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space\n|ideographic|inactive|infinite|inherit|initial|inline|inline-axis|inline-block|inline-end|inline-flex|inline-grid|inline-list-item|inline-start\n|inline-table|inset|inside|inter-character|inter-ideograph|inter-word|intersect|invert|isolate|isolate-override|italic|jis04|jis78|jis83\n|jis90|justify|justify-all|kannada|keep-all|landscape|large|larger|left|light|lighten|lighter|line|line-edge|line-through|linear|linearRGB\n|lining-nums|list-item|local|loose|lowercase|lr|lr-tb|ltr|luminance|luminosity|main-size|mandatory|manipulation|manual|margin-box|match-parent\n|match-source|mathematical|max-content|medium|menu|message-box|middle|min-content|miter|mixed|move|multiply|n-resize|narrower|ne-resize\n|nearest-neighbor|nesw-resize|newspaper|no-change|no-clip|no-close-quote|no-common-ligatures|no-contextual|no-discretionary-ligatures\n|no-drop|no-historical-ligatures|no-open-quote|no-repeat|none|nonzero|normal|not-allowed|nowrap|ns-resize|numbers|numeric|nw-resize|nwse-resize\n|oblique|oldstyle-nums|open|open-quote|optimizeLegibility|optimizeQuality|optimizeSpeed|optional|ordinal|outset|outside|over|overlay|overline|padding\n|padding-box|page|painted|pan-down|pan-left|pan-right|pan-up|pan-x|pan-y|paused|petite-caps|pixelated|plaintext|pointer|portrait|pre|pre-line\n|pre-wrap|preserve-3d|progress|progressive|proportional-nums|proportional-width|proximity|radial|recto|region|relative|remove|repeat|repeat-[xy]\n|reset-size|reverse|revert|revert-layer|ridge|right|rl|rl-tb|round|row|row-resize|row-reverse|row-severse|rtl|ruby|ruby-base|ruby-base-container|ruby-text\n|ruby-text-container|run-in|running|s-resize|saturation|scale-down|screen|scroll|scroll-position|se-resize|semi-condensed|semi-expanded|separate\n|sesame|show|sideways|sideways-left|sideways-lr|sideways-right|sideways-rl|simplified|slashed-zero|slice|small|small-caps|small-caption|smaller\n|smooth|soft-light|solid|space|space-around|space-between|space-evenly|spell-out|square|sRGB|stacked-fractions|start|static|status-bar|swap\n|step-end|step-start|sticky|stretch|strict|stroke|stroke-box|style|sub|subgrid|subpixel-antialiased|subtract|super|sw-resize|symbolic|table\n|table-caption|table-cell|table-column|table-column-group|table-footer-group|table-header-group|table-row|table-row-group|tabular-nums|tb|tb-rl\n|text|text-after-edge|text-before-edge|text-bottom|text-top|thick|thin|titling-caps|top|top-outside|touch|traditional|transparent|triangle\n|ultra-condensed|ultra-expanded|under|underline|unicase|unset|upleft|uppercase|upright|use-glyph-orientation|use-script|verso|vertical\n|vertical-ideographic|vertical-lr|vertical-rl|vertical-text|view-box|visible|visibleFill|visiblePainted|visibleStroke|w-resize|wait|wavy\n|weight|whitespace|wider|words|wrap|wrap-reverse|x|x-large|x-small|xx-large|xx-small|y|zero|zoom-in|zoom-out)\n(?![\\w-])",
"name": "support.constant.property-value.css"
},
{
+1 -1
View File
@@ -6,7 +6,7 @@
"git": {
"name": "dart-lang/dart-syntax-highlight",
"repositoryUrl": "https://github.com/dart-lang/dart-syntax-highlight",
"commitHash": "e1ac5c446c2531343393adbe8fff9d45d8a7c412"
"commitHash": "b2e04fbe2334bfe56940106b652f4c5799affbb1"
}
},
"licenseDetail": [
+59 -6
View File
@@ -4,7 +4,7 @@
"If you want to provide a fix or improvement, please create a pull request against the original repository.",
"Once accepted there, we are happy to receive an update request."
],
"version": "https://github.com/dart-lang/dart-syntax-highlight/commit/e1ac5c446c2531343393adbe8fff9d45d8a7c412",
"version": "https://github.com/dart-lang/dart-syntax-highlight/commit/b2e04fbe2334bfe56940106b652f4c5799affbb1",
"name": "Dart",
"scopeName": "source.dart",
"patterns": [
@@ -58,6 +58,12 @@
{
"include": "#constants-and-special-vars"
},
{
"include": "#class-identifier-with-optional-factory-method"
},
{
"include": "#function-identifier"
},
{
"include": "#operators"
},
@@ -224,12 +230,49 @@
{
"name": "constant.numeric.dart",
"match": "(?<!\\$)\\b((0(x|X)[0-9a-fA-F][0-9a-fA-F_]*)|(([0-9][0-9_]*\\.?[0-9_]*)|(\\.[0-9][0-9_]*))((e|E)(\\+|-)?[0-9][0-9_]*)?)\\b(?!\\$)"
}
]
},
"class-identifier-with-optional-factory-method": {
"patterns": [
{
"match": "(?<!\\$)\\b(bool|num|int|double|dynamic)\\b(?!\\$)\\s*(factory\\b)?",
"captures": {
"1": {
"name": "support.class.dart"
},
"2": {
"name": "entity.name.function.dart"
}
}
},
{
"include": "#class-identifier"
"match": "(?<!\\$)\\b(void)\\b(?!\\$)\\s*(factory\\b)?",
"captures": {
"1": {
"name": "storage.type.primitive.dart"
},
"2": {
"name": "entity.name.function.dart"
}
}
},
{
"include": "#function-identifier"
"begin": "(?<![a-zA-Z0-9_$])([_$]*[A-Z][a-zA-Z0-9_$]*)\\b\\s*(factory\\b)?",
"end": "(?!<)",
"beginCaptures": {
"1": {
"name": "support.class.dart"
},
"2": {
"name": "entity.name.function.dart"
}
},
"patterns": [
{
"include": "#type-args"
}
]
}
]
},
@@ -275,6 +318,10 @@
]
}
}
},
{
"match": "(?<=\\.)new\\b",
"name": "entity.name.function.dart"
}
]
},
@@ -330,8 +377,8 @@
"match": "(?<!\\$)\\bassert\\b(?!\\$)"
},
{
"name": "keyword.control.new.dart",
"match": "(?<!\\$)\\b(new)\\b(?!\\$)"
"name": "keyword.new.dart",
"match": "(?<![\\$\\.])\\b(new)\\b(?!\\$)"
},
{
"name": "keyword.control.return.dart",
@@ -347,7 +394,7 @@
},
{
"name": "storage.type.primitive.dart",
"match": "(?<!\\$)\\b(?:void|var)\\b(?!\\$)"
"match": "(?<!\\$)\\b(?:var)\\b(?!\\$)"
}
]
},
@@ -404,6 +451,12 @@
{
"include": "#constants-and-special-vars"
},
{
"include": "#class-identifier-with-optional-factory-method"
},
{
"include": "#function-identifier"
},
{
"include": "#strings"
},
@@ -15,4 +15,7 @@ run({
},
srcDir,
outdir: outDir,
additionalOptions: {
tsconfig: path.join(import.meta.dirname, 'tsconfig.browser.json'),
},
}, process.argv);
@@ -5,11 +5,14 @@
import * as vscode from 'vscode';
import { PackageDocument } from './packageDocumentHelper';
import { PackageDocumentL10nSupport } from './packageDocumentL10nSupport';
export function activate(context: vscode.ExtensionContext) {
//package.json suggestions
context.subscriptions.push(registerPackageDocumentCompletions());
//package.json go to definition for NLS strings
context.subscriptions.push(new PackageDocumentL10nSupport());
}
function registerPackageDocumentCompletions(): vscode.Disposable {
@@ -18,5 +21,4 @@ function registerPackageDocumentCompletions(): vscode.Disposable {
return new PackageDocument(document).provideCompletionItems(position, token);
}
});
}
@@ -5,6 +5,7 @@
import * as vscode from 'vscode';
import { PackageDocument } from './packageDocumentHelper';
import { PackageDocumentL10nSupport } from './packageDocumentL10nSupport';
import { ExtensionLinter } from './extensionLinter';
export function activate(context: vscode.ExtensionContext) {
@@ -15,6 +16,9 @@ export function activate(context: vscode.ExtensionContext) {
//package.json code actions for lint warnings
context.subscriptions.push(registerCodeActionsProvider());
// package.json l10n support
context.subscriptions.push(new PackageDocumentL10nSupport());
context.subscriptions.push(new ExtensionLinter());
}
@@ -0,0 +1,204 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { getLocation, getNodeValue, parseTree, findNodeAtLocation, visit } from 'jsonc-parser';
const packageJsonSelector: vscode.DocumentSelector = { language: 'json', pattern: '**/package.json' };
const packageNlsJsonSelector: vscode.DocumentSelector = { language: 'json', pattern: '**/package.nls.json' };
export class PackageDocumentL10nSupport implements vscode.DefinitionProvider, vscode.ReferenceProvider, vscode.Disposable {
private readonly _disposables: vscode.Disposable[] = [];
constructor() {
this._disposables.push(vscode.languages.registerDefinitionProvider(packageJsonSelector, this));
this._disposables.push(vscode.languages.registerDefinitionProvider(packageNlsJsonSelector, this));
this._disposables.push(vscode.languages.registerReferenceProvider(packageNlsJsonSelector, this));
this._disposables.push(vscode.languages.registerReferenceProvider(packageJsonSelector, this));
}
dispose(): void {
for (const d of this._disposables) {
d.dispose();
}
}
public async provideDefinition(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise<vscode.DefinitionLink[] | undefined> {
const basename = document.uri.path.split('/').pop()?.toLowerCase();
if (basename === 'package.json') {
return this.provideNlsValueDefinition(document, position);
}
if (basename === 'package.nls.json') {
return this.provideNlsKeyDefinition(document, position);
}
return undefined;
}
private async provideNlsValueDefinition(packageJsonDoc: vscode.TextDocument, position: vscode.Position): Promise<vscode.DefinitionLink[] | undefined> {
const nlsRef = this.getNlsReferenceAtPosition(packageJsonDoc, position);
if (!nlsRef) {
return undefined;
}
const nlsUri = vscode.Uri.joinPath(packageJsonDoc.uri, '..', 'package.nls.json');
return this.resolveNlsDefinition(nlsRef, nlsUri);
}
private async provideNlsKeyDefinition(nlsDoc: vscode.TextDocument, position: vscode.Position): Promise<vscode.DefinitionLink[] | undefined> {
const nlsKey = this.getNlsKeyDefinitionAtPosition(nlsDoc, position);
if (!nlsKey) {
return undefined;
}
return this.resolveNlsDefinition(nlsKey, nlsDoc.uri);
}
private async resolveNlsDefinition(origin: { key: string; range: vscode.Range }, nlsUri: vscode.Uri): Promise<vscode.DefinitionLink[] | undefined> {
const target = await this.findNlsKeyDeclaration(origin.key, nlsUri);
if (!target) {
return undefined;
}
return [{
originSelectionRange: origin.range,
targetUri: target.uri,
targetRange: target.range,
}];
}
private getNlsReferenceAtPosition(packageJsonDoc: vscode.TextDocument, position: vscode.Position): { key: string; range: vscode.Range } | undefined {
const location = getLocation(packageJsonDoc.getText(), packageJsonDoc.offsetAt(position));
if (!location.previousNode || location.previousNode.type !== 'string') {
return undefined;
}
const value = getNodeValue(location.previousNode);
if (typeof value !== 'string') {
return undefined;
}
const match = value.match(/^%(.+)%$/);
if (!match) {
return undefined;
}
const nodeStart = packageJsonDoc.positionAt(location.previousNode.offset);
const nodeEnd = packageJsonDoc.positionAt(location.previousNode.offset + location.previousNode.length);
return { key: match[1], range: new vscode.Range(nodeStart, nodeEnd) };
}
public async provideReferences(document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, _token: vscode.CancellationToken): Promise<vscode.Location[] | undefined> {
const basename = document.uri.path.split('/').pop()?.toLowerCase();
if (basename === 'package.nls.json') {
return this.provideNlsKeyReferences(document, position, context);
}
if (basename === 'package.json') {
return this.provideNlsValueReferences(document, position, context);
}
return undefined;
}
private async provideNlsKeyReferences(nlsDoc: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext): Promise<vscode.Location[] | undefined> {
const nlsKey = this.getNlsKeyDefinitionAtPosition(nlsDoc, position);
if (!nlsKey) {
return undefined;
}
const packageJsonUri = vscode.Uri.joinPath(nlsDoc.uri, '..', 'package.json');
return this.findAllNlsReferences(nlsKey.key, packageJsonUri, nlsDoc.uri, context);
}
private async provideNlsValueReferences(packageJsonDoc: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext): Promise<vscode.Location[] | undefined> {
const nlsRef = this.getNlsReferenceAtPosition(packageJsonDoc, position);
if (!nlsRef) {
return undefined;
}
const nlsUri = vscode.Uri.joinPath(packageJsonDoc.uri, '..', 'package.nls.json');
return this.findAllNlsReferences(nlsRef.key, packageJsonDoc.uri, nlsUri, context);
}
private async findAllNlsReferences(nlsKey: string, packageJsonUri: vscode.Uri, nlsUri: vscode.Uri, context: vscode.ReferenceContext): Promise<vscode.Location[]> {
const locations = await this.findNlsReferencesInPackageJson(nlsKey, packageJsonUri);
if (context.includeDeclaration) {
const decl = await this.findNlsKeyDeclaration(nlsKey, nlsUri);
if (decl) {
locations.push(decl);
}
}
return locations;
}
private async findNlsKeyDeclaration(nlsKey: string, nlsUri: vscode.Uri): Promise<vscode.Location | undefined> {
try {
const nlsDoc = await vscode.workspace.openTextDocument(nlsUri);
const nlsTree = parseTree(nlsDoc.getText());
if (!nlsTree) {
return undefined;
}
const node = findNodeAtLocation(nlsTree, [nlsKey]);
if (!node?.parent) {
return undefined;
}
const keyNode = node.parent.children?.[0];
if (!keyNode) {
return undefined;
}
const start = nlsDoc.positionAt(keyNode.offset);
const end = nlsDoc.positionAt(keyNode.offset + keyNode.length);
return new vscode.Location(nlsUri, new vscode.Range(start, end));
} catch {
return undefined;
}
}
private async findNlsReferencesInPackageJson(nlsKey: string, packageJsonUri: vscode.Uri): Promise<vscode.Location[]> {
let packageJsonDoc: vscode.TextDocument;
try {
packageJsonDoc = await vscode.workspace.openTextDocument(packageJsonUri);
} catch {
return [];
}
const text = packageJsonDoc.getText();
const needle = `%${nlsKey}%`;
const locations: vscode.Location[] = [];
visit(text, {
onLiteralValue(value, offset, length) {
if (value === needle) {
const start = packageJsonDoc.positionAt(offset);
const end = packageJsonDoc.positionAt(offset + length);
locations.push(new vscode.Location(packageJsonUri, new vscode.Range(start, end)));
}
}
});
return locations;
}
private getNlsKeyDefinitionAtPosition(nlsDoc: vscode.TextDocument, position: vscode.Position): { key: string; range: vscode.Range } | undefined {
const location = getLocation(nlsDoc.getText(), nlsDoc.offsetAt(position));
// Must be on a top-level property key
if (location.path.length !== 1 || !location.isAtPropertyKey || !location.previousNode) {
return undefined;
}
const key = location.path[0] as string;
const start = nlsDoc.positionAt(location.previousNode.offset);
const end = nlsDoc.positionAt(location.previousNode.offset + location.previousNode.length);
return { key, range: new vscode.Range(start, end) };
}
}
+1 -1
View File
@@ -3,5 +3,5 @@ test/**
out/**
tsconfig*.json
build/**
extension.webpack.config.js
esbuild*.mts
package-lock.json
+20
View File
@@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'node:path';
import { run } from '../esbuild-extension-common.mts';
const srcDir = path.join(import.meta.dirname, 'src');
const outDir = path.join(import.meta.dirname, 'dist');
run({
platform: 'node',
entryPoints: {
'main': path.join(srcDir, 'main.ts'),
'askpass-main': path.join(srcDir, 'askpass-main.ts'),
'git-editor-main': path.join(srcDir, 'git-editor-main.ts'),
},
srcDir,
outdir: outDir,
}, process.argv);
+112
View File
@@ -1147,6 +1147,46 @@
"title": "%command.deleteRef%",
"category": "Git",
"enablement": "!operationInProgress"
},
{
"command": "git.repositories.worktreeCopyBranchName",
"title": "%command.artifactCopyBranchName%",
"category": "Git"
},
{
"command": "git.repositories.worktreeCopyCommitHash",
"title": "%command.artifactCopyCommitHash%",
"category": "Git"
},
{
"command": "git.repositories.worktreeCopyPath",
"title": "%command.artifactCopyWorktreePath%",
"category": "Git"
},
{
"command": "git.repositories.copyCommitHash",
"title": "%command.artifactCopyCommitHash%",
"category": "Git"
},
{
"command": "git.repositories.copyBranchName",
"title": "%command.artifactCopyBranchName%",
"category": "Git"
},
{
"command": "git.repositories.copyTagName",
"title": "%command.artifactCopyTagName%",
"category": "Git"
},
{
"command": "git.repositories.copyStashName",
"title": "%command.artifactCopyStashName%",
"category": "Git"
},
{
"command": "git.repositories.stashCopyBranchName",
"title": "%command.artifactCopyBranchName%",
"category": "Git"
}
],
"continueEditSession": [
@@ -1846,6 +1886,38 @@
{
"command": "git.repositories.deleteWorktree",
"when": "false"
},
{
"command": "git.repositories.worktreeCopyBranchName",
"when": "false"
},
{
"command": "git.repositories.worktreeCopyCommitHash",
"when": "false"
},
{
"command": "git.repositories.worktreeCopyPath",
"when": "false"
},
{
"command": "git.repositories.copyCommitHash",
"when": "false"
},
{
"command": "git.repositories.copyBranchName",
"when": "false"
},
{
"command": "git.repositories.copyTagName",
"when": "false"
},
{
"command": "git.repositories.copyStashName",
"when": "false"
},
{
"command": "git.repositories.stashCopyBranchName",
"when": "false"
}
],
"scm/title": [
@@ -2090,6 +2162,16 @@
"group": "3_drop@3",
"when": "scmProvider == git && scmArtifactGroupId == stashes"
},
{
"command": "git.repositories.stashCopyBranchName",
"group": "4_copy@1",
"when": "scmProvider == git && scmArtifactGroupId == stashes"
},
{
"command": "git.repositories.copyStashName",
"group": "4_copy@2",
"when": "scmProvider == git && scmArtifactGroupId == stashes"
},
{
"command": "git.repositories.checkout",
"group": "1_checkout@1",
@@ -2130,6 +2212,21 @@
"group": "4_compare@1",
"when": "scmProvider == git && (scmArtifactGroupId == branches || scmArtifactGroupId == tags)"
},
{
"command": "git.repositories.copyCommitHash",
"group": "5_copy@2",
"when": "scmProvider == git && (scmArtifactGroupId == branches || scmArtifactGroupId == tags)"
},
{
"command": "git.repositories.copyBranchName",
"group": "5_copy@1",
"when": "scmProvider == git && scmArtifactGroupId == branches"
},
{
"command": "git.repositories.copyTagName",
"group": "5_copy@2",
"when": "scmProvider == git && scmArtifactGroupId == tags"
},
{
"command": "git.repositories.openWorktreeInNewWindow",
"group": "inline@1",
@@ -2149,6 +2246,21 @@
"command": "git.repositories.deleteWorktree",
"group": "2_modify@1",
"when": "scmProvider == git && scmArtifactGroupId == worktrees"
},
{
"command": "git.repositories.worktreeCopyCommitHash",
"group": "3_copy@2",
"when": "scmProvider == git && scmArtifactGroupId == worktrees"
},
{
"command": "git.repositories.worktreeCopyBranchName",
"group": "3_copy@1",
"when": "scmProvider == git && scmArtifactGroupId == worktrees"
},
{
"command": "git.repositories.worktreeCopyPath",
"group": "3_copy@3",
"when": "scmProvider == git && scmArtifactGroupId == worktrees"
}
],
"scm/resourceGroup/context": [
+6 -1
View File
@@ -129,7 +129,7 @@
"command.stashView": "View Stash...",
"command.stashView2": "View Stash",
"command.timelineOpenDiff": "Open Changes",
"command.timelineCopyCommitId": "Copy Commit ID",
"command.timelineCopyCommitId": "Copy Commit Hash",
"command.timelineCopyCommitMessage": "Copy Commit Message",
"command.timelineSelectForCompare": "Select for Compare",
"command.timelineCompareWithSelected": "Compare with Selected",
@@ -148,6 +148,11 @@
"command.graphCompareWithMergeBase": "Compare with Merge Base",
"command.graphCompareWithRemote": "Compare with Remote",
"command.deleteRef": "Delete",
"command.artifactCopyCommitHash": "Copy Commit Hash",
"command.artifactCopyBranchName": "Copy Branch Name",
"command.artifactCopyTagName": "Copy Tag Name",
"command.artifactCopyStashName": "Copy Stash Name",
"command.artifactCopyWorktreePath": "Copy Worktree Path",
"command.blameToggleEditorDecoration": "Toggle Git Blame Editor Decoration",
"command.blameToggleStatusBarItem": "Toggle Git Blame Status Bar Item",
"command.api.getRepositories": "Get Repositories",
+2 -1
View File
@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { Command, Disposable, Event, EventEmitter, SourceControlActionButton, Uri, workspace, l10n, LogOutputChannel } from 'vscode';
import { Branch, RefType, Status } from './api/git';
import type { Branch } from './api/git';
import { RefType, Status } from './api/git.constants';
import { OperationKind } from './operation';
import { CommitCommandsCenter } from './postCommitCommands';
import { Repository } from './repository';
+10 -1
View File
@@ -5,7 +5,8 @@
import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider, RefQuery, BranchProtectionProvider, InitOptions, SourceControlHistoryItemDetailsProvider, GitErrorCodes, CloneOptions, CommitShortStat, DiffChange, Worktree, RepositoryKind, RepositoryAccessDetails } from './git';
import type { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, LogOptions, APIState, CommitOptions, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider, RefQuery, BranchProtectionProvider, InitOptions, SourceControlHistoryItemDetailsProvider, CloneOptions, CommitShortStat, DiffChange, Worktree, RepositoryKind, RepositoryAccessDetails } from './git';
import { ForcePushMode, GitErrorCodes, RefType, Status } from './git.constants';
import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode';
import { combinedDisposable, filterEvent, mapEvent } from '../util';
import { toGitUri } from '../uri';
@@ -319,6 +320,10 @@ export class ApiRepository implements Repository {
return this.#repository.mergeAbort();
}
rebase(branch: string): Promise<void> {
return this.#repository.rebase(branch);
}
createStash(options?: { message?: string; includeUntracked?: boolean; staged?: boolean }): Promise<void> {
return this.#repository.createStash(options?.message, options?.includeUntracked, options?.staged);
}
@@ -346,6 +351,10 @@ export class ApiRepository implements Repository {
migrateChanges(sourceRepositoryPath: string, options?: { confirmation?: boolean; deleteFromSource?: boolean; untracked?: boolean }): Promise<void> {
return this.#repository.migrateChanges(sourceRepositoryPath, options);
}
generateRandomBranchName(): Promise<string | undefined> {
return this.#repository.generateRandomBranchName();
}
}
export class ApiGit implements Git {
+1 -1
View File
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Model } from '../model';
import { GitExtension, Repository, API } from './git';
import type { GitExtension, Repository, API } from './git';
import { ApiRepository, ApiImpl } from './api1';
import { Event, EventEmitter } from 'vscode';
import { CloneManager } from '../cloneManager';
+98
View File
@@ -0,0 +1,98 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type * as git from './git';
export type ForcePushMode = git.ForcePushMode;
export type RefType = git.RefType;
export type Status = git.Status;
export type GitErrorCodes = git.GitErrorCodes;
export const ForcePushMode = Object.freeze({
Force: 0,
ForceWithLease: 1,
ForceWithLeaseIfIncludes: 2,
}) satisfies typeof git.ForcePushMode;
export const RefType = Object.freeze({
Head: 0,
RemoteHead: 1,
Tag: 2,
}) satisfies typeof git.RefType;
export const Status = Object.freeze({
INDEX_MODIFIED: 0,
INDEX_ADDED: 1,
INDEX_DELETED: 2,
INDEX_RENAMED: 3,
INDEX_COPIED: 4,
MODIFIED: 5,
DELETED: 6,
UNTRACKED: 7,
IGNORED: 8,
INTENT_TO_ADD: 9,
INTENT_TO_RENAME: 10,
TYPE_CHANGED: 11,
ADDED_BY_US: 12,
ADDED_BY_THEM: 13,
DELETED_BY_US: 14,
DELETED_BY_THEM: 15,
BOTH_ADDED: 16,
BOTH_DELETED: 17,
BOTH_MODIFIED: 18,
}) satisfies typeof git.Status;
export const GitErrorCodes = Object.freeze({
BadConfigFile: 'BadConfigFile',
BadRevision: 'BadRevision',
AuthenticationFailed: 'AuthenticationFailed',
NoUserNameConfigured: 'NoUserNameConfigured',
NoUserEmailConfigured: 'NoUserEmailConfigured',
NoRemoteRepositorySpecified: 'NoRemoteRepositorySpecified',
NotAGitRepository: 'NotAGitRepository',
NotASafeGitRepository: 'NotASafeGitRepository',
NotAtRepositoryRoot: 'NotAtRepositoryRoot',
Conflict: 'Conflict',
StashConflict: 'StashConflict',
UnmergedChanges: 'UnmergedChanges',
PushRejected: 'PushRejected',
ForcePushWithLeaseRejected: 'ForcePushWithLeaseRejected',
ForcePushWithLeaseIfIncludesRejected: 'ForcePushWithLeaseIfIncludesRejected',
RemoteConnectionError: 'RemoteConnectionError',
DirtyWorkTree: 'DirtyWorkTree',
CantOpenResource: 'CantOpenResource',
GitNotFound: 'GitNotFound',
CantCreatePipe: 'CantCreatePipe',
PermissionDenied: 'PermissionDenied',
CantAccessRemote: 'CantAccessRemote',
RepositoryNotFound: 'RepositoryNotFound',
RepositoryIsLocked: 'RepositoryIsLocked',
BranchNotFullyMerged: 'BranchNotFullyMerged',
NoRemoteReference: 'NoRemoteReference',
InvalidBranchName: 'InvalidBranchName',
BranchAlreadyExists: 'BranchAlreadyExists',
NoLocalChanges: 'NoLocalChanges',
NoStashFound: 'NoStashFound',
LocalChangesOverwritten: 'LocalChangesOverwritten',
NoUpstreamBranch: 'NoUpstreamBranch',
IsInSubmodule: 'IsInSubmodule',
WrongCase: 'WrongCase',
CantLockRef: 'CantLockRef',
CantRebaseMultipleBranches: 'CantRebaseMultipleBranches',
PatchDoesNotApply: 'PatchDoesNotApply',
NoPathFound: 'NoPathFound',
UnknownPath: 'UnknownPath',
EmptyCommitMessage: 'EmptyCommitMessage',
BranchFastForwardRejected: 'BranchFastForwardRejected',
BranchNotYetBorn: 'BranchNotYetBorn',
TagConflict: 'TagConflict',
CherryPickEmpty: 'CherryPickEmpty',
CherryPickConflict: 'CherryPickConflict',
WorktreeContainsChanges: 'WorktreeContainsChanges',
WorktreeAlreadyExists: 'WorktreeAlreadyExists',
WorktreeBranchAlreadyUsed: 'WorktreeBranchAlreadyUsed',
}) satisfies Record<keyof typeof git.GitErrorCodes, string>;

Some files were not shown because too many files have changed in this diff Show More